Add request/approve communites
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
|
@ -1,5 +1,6 @@
|
|||
{:lint-as {status-im.utils.views/defview clojure.core/defn
|
||||
status-im.utils.views/letsubs clojure.core/let
|
||||
reagent.core/with-let clojkure.core/let
|
||||
status-im.utils.fx/defn clj-kondo.lint-as/def-catch-all
|
||||
quo.react/with-deps-check clojure.core/fn
|
||||
quo.previews.preview/list-comp clojure.core/for
|
||||
|
|
2
.env.e2e
|
@ -27,5 +27,5 @@ MAX_IMAGES_BATCH=5
|
|||
APN_TOPIC=im.status.ethereum.pr
|
||||
VERIFY_TRANSACTION_CHAIN_ID=3
|
||||
COMMUNITIES_ENABLED=1
|
||||
COMMUNITIES_MANAGEMENT_ENABLED=0
|
||||
DATABASE_MANAGEMENT_ENABLED=1
|
||||
COMMUNITIES_MANAGEMENT_ENABLED=1
|
||||
|
|
|
@ -27,5 +27,5 @@ BLANK_PREVIEW=0
|
|||
MAX_IMAGES_BATCH=5
|
||||
GOOGLE_FREE=0
|
||||
COMMUNITIES_ENABLED=1
|
||||
COMMUNITIES_MANAGEMENT_ENABLED=0
|
||||
DATABASE_MANAGEMENT_ENABLED=1
|
||||
COMMUNITIES_MANAGEMENT_ENABLED=1
|
||||
|
|
|
@ -22,3 +22,4 @@ MAX_IMAGES_BATCH=5
|
|||
BLANK_PREVIEW=0
|
||||
COMMUNITIES_ENABLED=1
|
||||
DATABASE_MANAGEMENT_ENABLED=1
|
||||
COMMUNITIES_MANAGEMENT_ENABLED=1
|
||||
|
|
After Width: | Height: | Size: 766 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 731 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 547 B |
After Width: | Height: | Size: 747 B |
After Width: | Height: | Size: 688 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 698 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 650 B |
After Width: | Height: | Size: 950 B |
After Width: | Height: | Size: 881 B |
After Width: | Height: | Size: 1.3 KiB |
|
@ -1,11 +1,14 @@
|
|||
(ns quo.components.list.footer
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.components.text :as text]))
|
||||
[quo.components.text :as text]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn footer [& children]
|
||||
(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))}
|
||||
(into [text/text {:color :secondary}]
|
||||
children)])
|
||||
|
||||
(into [text/text {:color color}]
|
||||
(reagent/children this))]))
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
(ns quo.components.list.header
|
||||
(:require [quo.react-native :as rn]
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.components.text :as text]))
|
||||
|
||||
(defn header [& children]
|
||||
(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 :secondary
|
||||
(into [text/text {:color color
|
||||
:style {:margin-top 10}}]
|
||||
children)])
|
||||
(reagent/children this))]))
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
(ns quo.components.list.index
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.components.text :as text]
|
||||
[quo.design-system.colors :as colors]))
|
||||
|
||||
(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}}
|
||||
[text/text title]]])
|
|
@ -9,6 +9,7 @@
|
|||
[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]
|
||||
|
@ -23,6 +24,7 @@
|
|||
(def list-header list-header/header)
|
||||
(def list-footer list-footer/footer)
|
||||
(def list-item list-item/list-item)
|
||||
(def list-index list-index/index)
|
||||
(def bottom-sheet bottom-sheet/bottom-sheet)
|
||||
(def switch controls/switch)
|
||||
(def radio controls/radio)
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
(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))
|
||||
|
||||
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn)))
|
||||
(def modal (reagent/adapt-react-class (.-Modal ^js rn)))
|
||||
(def refresh-control (reagent/adapt-react-class (.-RefreshControl ^js rn)))
|
||||
|
@ -90,9 +92,9 @@
|
|||
;; Flat-list
|
||||
(def ^:private rn-flat-list (reagent/adapt-react-class (.-FlatList ^js rn)))
|
||||
|
||||
(defn- wrap-render-fn [f]
|
||||
(defn- wrap-render-fn [f render-data]
|
||||
(fn [data]
|
||||
(reagent/as-element [f (.-item ^js data) (.-index ^js data) (.-separators ^js data)])))
|
||||
(reagent/as-element [f (.-item ^js data) (.-index ^js data) (.-separators ^js data) render-data])))
|
||||
|
||||
(defn- wrap-key-fn [f]
|
||||
(fn [data index]
|
||||
|
@ -100,10 +102,10 @@
|
|||
(f data index)))
|
||||
|
||||
(defn base-list-props
|
||||
[{:keys [key-fn render-fn empty-component header footer separator data] :as props}]
|
||||
[{:keys [key-fn render-fn empty-component header footer separator data render-data] :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)})
|
||||
(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)})
|
||||
|
|
|
@ -211,10 +211,11 @@
|
|||
;;;; Send message
|
||||
(fx/defn update-db-message-status
|
||||
[{:keys [db] :as cofx} chat-id message-id status]
|
||||
(when (get-in db [:messages chat-id message-id])
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db
|
||||
[:messages chat-id message-id :outgoing-status]
|
||||
status)}))
|
||||
status)})))
|
||||
|
||||
(fx/defn update-message-status
|
||||
[{:keys [db] :as cofx} chat-id message-id status]
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
(:require
|
||||
[re-frame.core :as re-frame]
|
||||
[clojure.walk :as walk]
|
||||
[clojure.string :as string]
|
||||
[clojure.set :as clojure.set]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.constants :as constants]
|
||||
|
@ -9,35 +11,50 @@
|
|||
[status-im.transport.filters.core :as models.filters]
|
||||
[status-im.bottom-sheet.core :as bottom-sheet]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]))
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.navigation :as navigation]))
|
||||
|
||||
(def crop-size 1000)
|
||||
|
||||
(def featured
|
||||
[{:name "Status"
|
||||
:id constants/status-community-id}])
|
||||
|
||||
(def access-no-membership 1)
|
||||
(def access-invitation-only 2)
|
||||
(def access-on-request 3)
|
||||
(defn <-request-to-join-community-rpc [r]
|
||||
(clojure.set/rename-keys r {:communityId :community-id
|
||||
:publicKey :public-key
|
||||
:chatId :chat-id}))
|
||||
|
||||
(defn <-requests-to-join-community-rpc [requests]
|
||||
(reduce (fn [acc r]
|
||||
(assoc acc (:id r) (<-request-to-join-community-rpc r)))
|
||||
{}
|
||||
requests))
|
||||
|
||||
(defn <-chats-rpc [chats]
|
||||
(reduce-kv (fn [acc k v]
|
||||
(assoc acc
|
||||
(name k)
|
||||
(-> v
|
||||
(update :members walk/stringify-keys)
|
||||
(assoc :identity {:display-name (get-in v [:identity :display_name])
|
||||
:description (get-in v [:identity :description])}
|
||||
:id (name k)))))
|
||||
(assoc :can-post? (:canPost v))
|
||||
(dissoc :canPost)
|
||||
(update :members walk/stringify-keys))))
|
||||
{}
|
||||
chats))
|
||||
|
||||
(defn <-rpc [{:keys [description] :as c}]
|
||||
(let [identity (:identity description)]
|
||||
(defn <-rpc [c]
|
||||
(-> c
|
||||
(update-in [:description :members] walk/stringify-keys)
|
||||
(assoc-in [:description :identity] {:display-name (:display_name identity)
|
||||
:description (:description identity)})
|
||||
(update-in [:description :chats] <-chats-rpc))))
|
||||
(clojure.set/rename-keys {:canRequestAccess :can-request-access?
|
||||
:canManageUsers :can-manage-users?
|
||||
:canJoin :can-join?
|
||||
:requestedToJoinAt :requested-to-join-at
|
||||
:isMember :is-member?})
|
||||
(update :members walk/stringify-keys)
|
||||
(update :chats <-chats-rpc)))
|
||||
|
||||
(defn fetch-community-id-input [{:keys [db]}]
|
||||
(:communities/community-id-input db))
|
||||
|
||||
(fx/defn handle-chats [cofx chats]
|
||||
(models.chat/ensure-chats cofx chats))
|
||||
|
@ -48,6 +65,10 @@
|
|||
(fx/defn handle-removed-filters [cofx filters]
|
||||
(models.filters/handle-filters-removed cofx (map models.filters/responses->filters filters)))
|
||||
|
||||
(fx/defn handle-request-to-join [{:keys [db]} r]
|
||||
(let [{:keys [id community-id] :as request} (<-request-to-join-community-rpc r)]
|
||||
{:db (assoc-in db [:communities/requests-to-join community-id id] request)}))
|
||||
|
||||
(fx/defn handle-removed-chats [{:keys [db]} chat-ids]
|
||||
{:db (reduce (fn [db chat-id]
|
||||
(update db :chats dissoc chat-id))
|
||||
|
@ -83,24 +104,27 @@
|
|||
(handle-response cofx response))
|
||||
|
||||
(fx/defn joined
|
||||
{:events [::joined]}
|
||||
{:events [::joined ::requested-to-join]}
|
||||
[cofx response]
|
||||
(handle-response cofx response))
|
||||
|
||||
(fx/defn export
|
||||
[cofx community-id on-success]
|
||||
{:events [::export-pressed]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_exportCommunity"
|
||||
:params [community-id]
|
||||
:on-success on-success
|
||||
:on-success #(re-frame/dispatch [:show-popover {:view :export-community
|
||||
:community-key %}])
|
||||
:on-error #(do
|
||||
(log/error "failed to export community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-export %]))}]})
|
||||
|
||||
(fx/defn import-community
|
||||
{:events [::import]}
|
||||
[cofx community-key on-success]
|
||||
[cofx community-key]
|
||||
{::json-rpc/call [{:method "wakuext_importCommunity"
|
||||
:params [community-key]
|
||||
:on-success on-success
|
||||
:on-success #(re-frame/dispatch [::community-imported %])
|
||||
:on-error #(do
|
||||
(log/error "failed to import community" %)
|
||||
(re-frame/dispatch [::failed-to-import %]))}]})
|
||||
|
@ -115,6 +139,16 @@
|
|||
(log/error "failed to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-join %]))}]})
|
||||
|
||||
(fx/defn request-to-join
|
||||
{:events [::request-to-join]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_requestToJoinCommunity"
|
||||
:params [{:communityId community-id}]
|
||||
:on-success #(re-frame/dispatch [::requested-to-join %])
|
||||
:on-error #(do
|
||||
(log/error "failed to request to join community" community-id %)
|
||||
(re-frame/dispatch [::failed-to-request-to-join %]))}]})
|
||||
|
||||
(fx/defn leave
|
||||
{:events [::leave]}
|
||||
[cofx community-id]
|
||||
|
@ -145,175 +179,256 @@
|
|||
#(re-frame/dispatch [:transport/message-sent % 1])
|
||||
:on-failure #(log/error "failed to send a message" %)}]})
|
||||
|
||||
(fx/defn invite-user [cofx
|
||||
community-id
|
||||
user-pk
|
||||
on-success-event
|
||||
on-failure-event]
|
||||
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "wakuext_inviteUserToCommunity"
|
||||
:params [community-id
|
||||
user-pk]
|
||||
:on-success #(re-frame/dispatch [on-success-event %])
|
||||
(fx/defn invite-users
|
||||
{:events [::invite-people-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
pks (if (seq user-pk)
|
||||
(conj contacts user-pk)
|
||||
contacts)]
|
||||
(when (seq pks)
|
||||
{::json-rpc/call [{:method "wakuext_inviteUsersToCommunity"
|
||||
:params [{:communityId community-id
|
||||
:users pks}]
|
||||
:on-success #(re-frame/dispatch [::people-invited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [on-failure-event %]))}]}
|
||||
(models.chat/upsert-chat {:chat-id user-pk
|
||||
:active (get-in cofx [:db :chats user-pk :active])}
|
||||
#(re-frame/dispatch [::chat-created community-id user-pk]))))
|
||||
(re-frame/dispatch [::failed-to-invite-people %]))}]})))
|
||||
(fx/defn share-community
|
||||
{:events [::share-community-confirmation-pressed]}
|
||||
[cofx user-pk contacts]
|
||||
(let [community-id (fetch-community-id-input cofx)
|
||||
pks (if (seq user-pk)
|
||||
(conj contacts user-pk)
|
||||
contacts)]
|
||||
(when (seq pks)
|
||||
{::json-rpc/call [{:method "wakuext_shareCommunity"
|
||||
:params [{:communityId community-id
|
||||
:users pks}]
|
||||
:on-success #(re-frame/dispatch [::people-invited %])
|
||||
:on-error #(do
|
||||
(log/error "failed to invite-user community" %)
|
||||
(re-frame/dispatch [::failed-to-share-community %]))}]})))
|
||||
|
||||
(fx/defn create [{:keys [db]}
|
||||
community-name
|
||||
community-description
|
||||
community-membership
|
||||
on-success-event
|
||||
on-failure-event]
|
||||
(let [membership (js/parseInt community-membership)
|
||||
(fx/defn create
|
||||
{:events [::create-confirmation-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [name description membership image]} (get db :communities/create)
|
||||
my-public-key (get-in db [:multiaccount :public-key])]
|
||||
;; If access is ENS only, we set the access to require approval and set the rule
|
||||
;; of ens only
|
||||
(let [params (cond-> {:name name
|
||||
:description description
|
||||
:membership (or membership constants/community-no-membership-access)
|
||||
:color (rand-nth colors/chat-colors)
|
||||
:image (string/replace-first (str image) #"file://" "")
|
||||
:imageAx 0
|
||||
:imageAy 0
|
||||
:imageBx crop-size
|
||||
:imageBy crop-size}
|
||||
(= membership constants/community-rule-ens-only)
|
||||
(assoc :membership constants/community-on-request-access
|
||||
:ens-only true))]
|
||||
|
||||
{::json-rpc/call [{:method "wakuext_createCommunity"
|
||||
:params [{:identity {:display_name community-name
|
||||
:description community-description}
|
||||
:members {my-public-key {}}
|
||||
:permissions {:access membership}}]
|
||||
:on-success #(re-frame/dispatch [on-success-event %])
|
||||
:params [params]
|
||||
:on-success #(re-frame/dispatch [::community-created %])
|
||||
:on-error #(do
|
||||
(log/error "failed to create community" %)
|
||||
(re-frame/dispatch [on-failure-event %]))}]}))
|
||||
(re-frame/dispatch [::failed-to-create-community %]))}]})))
|
||||
|
||||
(defn create-channel [community-id
|
||||
community-channel-name
|
||||
community-channel-description
|
||||
on-success-event
|
||||
on-failure-event]
|
||||
(fx/defn edit
|
||||
{:events [::edit-confirmation-pressed]}
|
||||
[{:keys [db]}]
|
||||
(let [{:keys [name description membership]} (get db :communities/create)
|
||||
my-public-key (get-in db [:multiaccount :public-key])]
|
||||
(log/error "Edit community is not yet implemented")
|
||||
;; {::json-rpc/call [{:method "wakuext_editCommunity"
|
||||
;; :params [{:identity {:display_name name
|
||||
;; :description description}
|
||||
;; :permissions {:access membership}}]
|
||||
;; :on-success #(re-frame/dispatch [::community-edited %])
|
||||
;; :on-error #(do
|
||||
;; (log/error "failed to create community" %)
|
||||
;; (re-frame/dispatch [::failed-to-edit-community %]))}]}
|
||||
))
|
||||
|
||||
(fx/defn create-channel
|
||||
{:events [::create-channel-confirmation-pressed]}
|
||||
[cofx community-channel-name community-channel-description]
|
||||
(let [community-id (fetch-community-id-input cofx)]
|
||||
{::json-rpc/call [{:method "wakuext_createCommunityChat"
|
||||
:params [community-id
|
||||
{:identity {:display_name community-channel-name
|
||||
:color (rand-nth colors/chat-colors)
|
||||
:description community-channel-description}
|
||||
:permissions {:access access-no-membership}}]
|
||||
:on-success #(re-frame/dispatch [on-success-event %])
|
||||
:permissions {:access constants/community-channel-access-no-membership}}]
|
||||
:on-success #(re-frame/dispatch [::community-channel-created %])
|
||||
:on-error #(do
|
||||
(log/error "failed to create community channel" %)
|
||||
(re-frame/dispatch [on-failure-event %]))}]})
|
||||
|
||||
(def no-membership-access 1)
|
||||
(def invitation-only-access 2)
|
||||
(def on-request-access 3)
|
||||
(re-frame/dispatch [::failed-to-create-community-channel %]))}]}))
|
||||
|
||||
(defn require-membership? [permissions]
|
||||
(not= no-membership-access (:access permissions)))
|
||||
(not= constants/community-no-membership-access (:access permissions)))
|
||||
|
||||
(def community-id-length 68)
|
||||
;; TODO: test this
|
||||
(defn can-post? [{:keys [admin] :as community} pk local-chat-id]
|
||||
(let [chat-id (subs local-chat-id community-id-length)
|
||||
can-access-community? (or (get-in community [:description :members pk])
|
||||
(not (require-membership? (get-in community [:description :permissions]))))]
|
||||
(or admin
|
||||
(get-in community [:description :chats chat-id :members pk])
|
||||
(and can-access-community?
|
||||
(not (require-membership? (get-in community [:description :chats chat-id :permissions])))))))
|
||||
|
||||
(defn can-post? [community _ local-chat-id]
|
||||
(let [chat-id (subs local-chat-id community-id-length)]
|
||||
(get-in community [:chats chat-id :can-post?])))
|
||||
|
||||
(fx/defn reset-community-id-input [{:keys [db]} id]
|
||||
{:db (assoc db :communities/community-id-input id)})
|
||||
|
||||
(defn fetch-community-id-input [{:keys [db]}]
|
||||
(:communities/community-id-input db))
|
||||
|
||||
(fx/defn import-pressed
|
||||
{:events [::import-pressed]}
|
||||
[cofx]
|
||||
(bottom-sheet/show-bottom-sheet cofx {:view :import-community}))
|
||||
|
||||
(fx/defn create-pressed
|
||||
{:events [::create-pressed]}
|
||||
[cofx]
|
||||
(bottom-sheet/show-bottom-sheet cofx {:view :create-community}))
|
||||
|
||||
(fx/defn invite-people-pressed
|
||||
{:events [::invite-people-pressed]}
|
||||
[cofx id]
|
||||
(fx/merge cofx
|
||||
(reset-community-id-input id)
|
||||
(bottom-sheet/show-bottom-sheet {:view :invite-people-community})))
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/navigate-to :invite-people-community {:invite? true})))
|
||||
|
||||
(fx/defn share-community-pressed
|
||||
{:events [::share-community-pressed]}
|
||||
[cofx id]
|
||||
(fx/merge cofx
|
||||
(reset-community-id-input id)
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/navigate-to :invite-people-community {})))
|
||||
|
||||
(fx/defn create-channel-pressed
|
||||
{:events [::create-channel-pressed]}
|
||||
[cofx id]
|
||||
(fx/merge cofx
|
||||
(reset-community-id-input id)
|
||||
(bottom-sheet/show-bottom-sheet {:view :create-community-channel})))
|
||||
(navigation/navigate-to :create-community-channel nil)))
|
||||
|
||||
(fx/defn community-created
|
||||
{:events [::community-created]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/navigate-back)
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn community-edited
|
||||
{:events [::community-edited]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn open-create-community
|
||||
{:events [::open-create-community]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :communities/create {})}
|
||||
(navigation/navigate-to :community-create nil)))
|
||||
|
||||
(fx/defn open-edit-community
|
||||
{:events [::open-edit-community]}
|
||||
[{:keys [db] :as cofx} id]
|
||||
(let [{:keys [identity permissions]} (get-in db [:communities id :description])
|
||||
{:keys [display-name description image]} identity
|
||||
{:keys [access]} permissions]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :communities/create {:name display-name
|
||||
:description description
|
||||
:image image
|
||||
:membership access})}
|
||||
(navigation/navigate-to :communities {:screen :community-edit}))))
|
||||
|
||||
(fx/defn community-imported
|
||||
{:events [::community-imported]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/navigate-back)
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn people-invited
|
||||
{:events [::people-invited]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(navigation/navigate-back)
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn community-channel-created
|
||||
{:events [::community-channel-created]}
|
||||
[cofx response]
|
||||
(fx/merge cofx
|
||||
(navigation/navigate-back)
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn create-field
|
||||
{:events [::create-field]}
|
||||
[{:keys [db]} field value]
|
||||
{:db (assoc-in db [:communities/create field] value)})
|
||||
|
||||
(fx/defn member-ban
|
||||
{:events [::member-ban]}
|
||||
[cofx community-id public-key]
|
||||
(log/error "Community member ban is not yet implemented"))
|
||||
|
||||
(fx/defn member-kicked
|
||||
{:events [::member-kicked]}
|
||||
[cofx response]
|
||||
|
||||
(fx/merge cofx
|
||||
(bottom-sheet/hide-bottom-sheet)
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn handle-export-pressed
|
||||
{:events [::export-pressed]}
|
||||
(fx/defn member-kick
|
||||
{:events [::member-kick]}
|
||||
[cofx community-id public-key]
|
||||
{::json-rpc/call [{:method "wakuext_removeUserFromCommunity"
|
||||
:params [community-id public-key]
|
||||
:on-success #(re-frame/dispatch [::member-kicked %])
|
||||
:on-error #(log/error "failed to remove user from community" community-id public-key %)}]})
|
||||
|
||||
(fx/defn delete-community
|
||||
{:events [::delete-community]}
|
||||
[cofx community-id]
|
||||
(export cofx community-id
|
||||
#(re-frame/dispatch [:show-popover {:view :export-community
|
||||
:community-key %}])))
|
||||
(log/error "Community delete is not yet implemented"))
|
||||
|
||||
(fx/defn import-confirmation-pressed
|
||||
{:events [::import-confirmation-pressed]}
|
||||
[cofx community-key]
|
||||
(import-community
|
||||
cofx
|
||||
community-key
|
||||
#(re-frame/dispatch [::community-imported %])))
|
||||
(fx/defn requests-to-join-fetched
|
||||
{:events [::requests-to-join-fetched]}
|
||||
[{:keys [db]} community-id requests]
|
||||
{:db (assoc-in db [:communities/requests-to-join community-id] (<-requests-to-join-community-rpc requests))})
|
||||
|
||||
(fx/defn create-confirmation-pressed
|
||||
{:events [::create-confirmation-pressed]}
|
||||
[cofx community-name community-description membership]
|
||||
(create
|
||||
cofx
|
||||
community-name
|
||||
community-description
|
||||
membership
|
||||
::community-created
|
||||
::failed-to-create-community))
|
||||
(fx/defn fetch-requests-to-join
|
||||
{:events [::fetch-requests-to-join]}
|
||||
[cofx community-id]
|
||||
{::json-rpc/call [{:method "wakuext_pendingRequestsToJoinForCommunity"
|
||||
:params [community-id]
|
||||
:on-success #(re-frame/dispatch [::requests-to-join-fetched community-id %])
|
||||
:on-error #(log/error "failed to fetch requests-to-join" community-id %)}]})
|
||||
|
||||
(fx/defn create-channel-confirmation-pressed
|
||||
{:events [::create-channel-confirmation-pressed]}
|
||||
[cofx community-channel-name community-channel-description]
|
||||
(create-channel
|
||||
(fetch-community-id-input cofx)
|
||||
community-channel-name
|
||||
community-channel-description
|
||||
::community-channel-created
|
||||
::failed-to-create-community-channel))
|
||||
(defn fetch-requests-to-join! [community-id]
|
||||
(re-frame/dispatch [::fetch-requests-to-join community-id]))
|
||||
|
||||
(fx/defn invite-people-confirmation-pressed
|
||||
{:events [::invite-people-confirmation-pressed]}
|
||||
[cofx user-pk]
|
||||
(invite-user
|
||||
cofx
|
||||
(fetch-community-id-input cofx)
|
||||
user-pk
|
||||
::people-invited
|
||||
::failed-to-invite-people))
|
||||
(fx/defn request-to-join-accepted
|
||||
{:events [::request-to-join-accepted]}
|
||||
[{:keys [db] :as cofx} community-id request-id response]
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)}
|
||||
(handle-response response)))
|
||||
|
||||
(fx/defn request-to-join-declined
|
||||
{:events [::request-to-join-declined]}
|
||||
[{:keys [db] :as cofx} community-id request-id]
|
||||
{:db (update-in db [:communities/requests-to-join community-id] dissoc request-id)})
|
||||
|
||||
(fx/defn accept-request-to-join-pressed
|
||||
{:events [:communities.ui/accept-request-to-join-pressed]}
|
||||
[cofx community-id request-id]
|
||||
{::json-rpc/call [{:method "wakuext_acceptRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
:on-success #(re-frame/dispatch [::request-to-join-accepted community-id request-id %])
|
||||
:on-error #(log/error "failed to accept requests-to-join" community-id request-id %)}]})
|
||||
|
||||
(fx/defn decline-request-to-join-pressed
|
||||
{:events [:communities.ui/decline-request-to-join-pressed]}
|
||||
[cofx community-id request-id]
|
||||
{::json-rpc/call [{:method "wakuext_declineRequestToJoinCommunity"
|
||||
:params [{:id request-id}]
|
||||
:on-success #(re-frame/dispatch [::request-to-join-declined community-id request-id %])
|
||||
:on-error #(log/error "failed to decline requests-to-join" community-id request-id)}]})
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
(def ^:const timeline-chat-type 5)
|
||||
(def ^:const community-chat-type 6)
|
||||
|
||||
(def request-to-join-pending-state 1)
|
||||
|
||||
(def reactions {emoji-reaction-love (:love resources/reactions)
|
||||
emoji-reaction-thumbs-up (:thumbs-up resources/reactions)
|
||||
emoji-reaction-thumbs-down (:thumbs-down resources/reactions)
|
||||
|
@ -79,6 +81,17 @@
|
|||
|
||||
(def ^:const status-create-address "status_createaddress")
|
||||
|
||||
(def ^:const community-no-membership-access 1)
|
||||
(def ^:const community-invitation-only-access 2)
|
||||
(def ^:const community-on-request-access 3)
|
||||
|
||||
;; Community rules for joining
|
||||
(def ^:const community-rule-ens-only "ens-only")
|
||||
|
||||
(def ^:const community-channel-access-no-membership 1)
|
||||
(def ^:const community-channel-access-invitation-only 2)
|
||||
(def ^:const community-channel-access-on-request 3)
|
||||
|
||||
; BIP44 Wallet Root Key, the extended key from which any wallet can be derived
|
||||
(def ^:const path-wallet-root "m/44'/60'/0'/0")
|
||||
; EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived
|
||||
|
|
|
@ -123,13 +123,17 @@
|
|||
(fx/defn name-verified
|
||||
{:events [:contacts/ens-name-verified]}
|
||||
[{:keys [db now] :as cofx} public-key ens-name]
|
||||
(let [contact (-> (get-in db [:contacts/contacts public-key]
|
||||
(let [contact (-> (or (get-in db [:contacts/contacts public-key])
|
||||
(build-contact cofx public-key))
|
||||
(assoc :name ens-name
|
||||
:last-ens-clock-value now
|
||||
:ens-verified-at now
|
||||
:ens-verified true))]
|
||||
(upsert-contact cofx contact)))
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(update-in [:contacts/contacts public-key] merge contact))
|
||||
::json-rpc/call [{:method "wakuext_ensVerified"
|
||||
:params [public-key ens-name]
|
||||
:on-success #(log/debug "ens name verified successuful")}]}
|
||||
(transport.filters/load-contact contact))))
|
||||
|
||||
(fx/defn update-nickname
|
||||
{:events [:contacts/update-nickname]}
|
||||
|
|
|
@ -117,12 +117,19 @@
|
|||
"multiaccounts_deleteIdentityImage" {}
|
||||
"wakuext_createCommunity" {}
|
||||
"wakuext_createCommunityChat" {}
|
||||
"wakuext_inviteUserToCommunity" {}
|
||||
"wakuext_inviteUsersToCommunity" {}
|
||||
"wakuext_shareCommunity" {}
|
||||
"wakuext_removeUserFromCommunity" {}
|
||||
"wakuext_requestToJoinCommunity" {}
|
||||
"wakuext_acceptRequestToJoinCommunity" {}
|
||||
"wakuext_declineRequestToJoinCommunity" {}
|
||||
"wakuext_pendingRequestsToJoinForCommunity" {}
|
||||
"wakuext_joinCommunity" {}
|
||||
"wakuext_leaveCommunity" {}
|
||||
"wakuext_communities" {}
|
||||
"wakuext_importCommunity" {}
|
||||
"wakuext_exportCommunity" {}
|
||||
"wakuext_ensVerified" {}
|
||||
"status_chats" {}
|
||||
"localnotifications_switchWalletNotifications" {}
|
||||
"localnotifications_notificationPreferences" {}
|
||||
|
|
|
@ -213,6 +213,11 @@
|
|||
(reg-root-key-sub :buy-crypto/on-ramps :buy-crypto/on-ramps)
|
||||
|
||||
;; communities
|
||||
|
||||
(reg-root-key-sub :communities/create :communities/create)
|
||||
(reg-root-key-sub :communities/requests-to-join :communities/requests-to-join)
|
||||
(reg-root-key-sub :communities/community-id-input :communities/community-id-input)
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities
|
||||
(fn [db]
|
||||
|
@ -225,6 +230,17 @@
|
|||
:else
|
||||
[])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/section-list
|
||||
:<- [:communities]
|
||||
(fn [communities]
|
||||
(->> (vals communities)
|
||||
(group-by (comp (fnil string/upper-case "") first :name))
|
||||
(sort-by (fn [[title]] title))
|
||||
(map (fn [[title data]]
|
||||
{:title title
|
||||
:data data})))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/community
|
||||
:<- [:communities]
|
||||
|
@ -232,16 +248,25 @@
|
|||
(get communities id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/status-community
|
||||
:communities/communities
|
||||
:<- [:search/home-filter]
|
||||
:<- [:communities]
|
||||
(fn [[search-filter communities]]
|
||||
(let [status-community (get communities constants/status-community-id)]
|
||||
(when (and (:joined status-community)
|
||||
(or (string/blank? search-filter)
|
||||
(string/includes? (string/lower-case
|
||||
(get-in status-community [:description :identity :display-name])) search-filter)))
|
||||
status-community))))
|
||||
(filterv
|
||||
(fn [{:keys [name joined id]}]
|
||||
(and joined
|
||||
(or config/communities-management-enabled?
|
||||
(= id constants/status-community-id))
|
||||
(or (empty? search-filter)
|
||||
(string/includes? (string/lower-case (str name)) search-filter))))
|
||||
(vals communities))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/edited-community
|
||||
:<- [:communities]
|
||||
:<- [:communities/community-id-input]
|
||||
(fn [[communities community-id]]
|
||||
(get communities community-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/current-community
|
||||
|
@ -260,6 +285,16 @@
|
|||
0
|
||||
chats)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:communities/requests-to-join-for-community
|
||||
:<- [:communities/requests-to-join]
|
||||
(fn [requests [_ community-id]]
|
||||
(->>
|
||||
(get requests community-id {})
|
||||
vals
|
||||
(filter (fn [{:keys [state]}]
|
||||
(= state constants/request-to-join-pending-state))))))
|
||||
|
||||
;;GENERAL ==============================================================================================================
|
||||
|
||||
|
||||
|
@ -1198,9 +1233,21 @@
|
|||
:home-items
|
||||
:<- [:search/home-filter]
|
||||
:<- [:search/filtered-chats]
|
||||
(fn [[search-filter filtered-chats]]
|
||||
:<- [:communities/communities]
|
||||
(fn [[search-filter filtered-chats communities]]
|
||||
(let [communities-count (count communities)
|
||||
chats-count (count filtered-chats)
|
||||
;; If we have both communities & chats we want to display
|
||||
;; a separator between them
|
||||
|
||||
communities-with-separator (if (and (pos? communities-count)
|
||||
(pos? chats-count))
|
||||
(update communities
|
||||
(dec communities-count)
|
||||
assoc :last? true)
|
||||
communities)]
|
||||
{:search-filter search-filter
|
||||
:chats filtered-chats}))
|
||||
:items (concat communities-with-separator filtered-chats)})))
|
||||
|
||||
;;PAIRING ==============================================================================================================
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
(fx/defn handle-community [cofx community]
|
||||
(models.communities/handle-community cofx community))
|
||||
|
||||
(fx/defn handle-request-to-join-community [cofx request]
|
||||
(models.communities/handle-request-to-join cofx request))
|
||||
|
||||
(fx/defn handle-reactions [cofx reactions]
|
||||
(models.reactions/receive-signal cofx reactions))
|
||||
|
||||
|
@ -44,6 +47,7 @@
|
|||
{:events [::process]}
|
||||
[cofx ^js response-js]
|
||||
(let [^js communities (.-communities response-js)
|
||||
^js requests-to-join-community (.-requestsToJoinCommunity response-js)
|
||||
^js chats (.-chats response-js)
|
||||
^js contacts (.-contacts response-js)
|
||||
^js installations (.-installations response-js)
|
||||
|
@ -72,6 +76,11 @@
|
|||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-community (types/js->clj community))))
|
||||
(seq requests-to-join-community)
|
||||
(let [request (.pop requests-to-join-community)]
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-request-to-join-community (types/js->clj request))))
|
||||
(seq chats)
|
||||
(let [chats-clj (types/js->clj chats)]
|
||||
(js-delete response-js "chats")
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
(ns status-im.ui.components.unviewed-indicator
|
||||
(:require [status-im.ui.components.badge :as badge]
|
||||
[status-im.ui.components.react :as react]))
|
||||
|
||||
(defn unviewed-indicator [c]
|
||||
(when (pos? c)
|
||||
[react/view {:padding-left 16
|
||||
:justify-content :flex-end
|
||||
:align-items :flex-end}
|
||||
[badge/message-counter c]]))
|
|
@ -4,7 +4,6 @@
|
|||
[status-im.ui.screens.home.sheet.views :as home.sheet]
|
||||
[status-im.ui.screens.keycard.views :as keycard]
|
||||
[status-im.ui.screens.about-app.views :as about-app]
|
||||
[status-im.ui.screens.communities.views :as communities]
|
||||
[status-im.ui.screens.multiaccounts.recover.views :as recover.views]
|
||||
[quo.core :as quo]))
|
||||
|
||||
|
@ -33,18 +32,6 @@
|
|||
(= view :learn-more)
|
||||
(merge about-app/learn-more)
|
||||
|
||||
(= view :create-community)
|
||||
(merge communities/create-sheet)
|
||||
|
||||
(= view :import-community)
|
||||
(merge communities/import-sheet)
|
||||
|
||||
(= view :create-community-channel)
|
||||
(merge communities/create-channel-sheet)
|
||||
|
||||
(= view :invite-people-community)
|
||||
(merge communities/invite-people-sheet)
|
||||
|
||||
(= view :recover-sheet)
|
||||
(merge recover.views/bottom-sheet))]
|
||||
[quo/bottom-sheet opts
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
|
@ -216,7 +215,7 @@
|
|||
(chat.utils/format-author contact-with-names opts)))
|
||||
|
||||
(defview community-content [{:keys [community-id] :as message}]
|
||||
(letsubs [{:keys [joined verified] :as community} [:communities/community community-id]]
|
||||
(letsubs [{:keys [name description verified] :as community} [:communities/community community-id]]
|
||||
(when (and
|
||||
config/communities-enabled?
|
||||
community)
|
||||
|
@ -253,25 +252,24 @@
|
|||
:style {:width 40
|
||||
:height 40}}]
|
||||
|
||||
(let [display-name (get-in community [:description :identity :display-name])]
|
||||
[chat-icon/chat-icon-view-chat-list
|
||||
display-name
|
||||
name
|
||||
true
|
||||
display-name
|
||||
colors/default-community-color]))]
|
||||
name
|
||||
colors/default-community-color])]
|
||||
[react/view {:padding-right 14}
|
||||
[react/text {:style {:font-weight "700"
|
||||
:font-size 17}}
|
||||
(get-in community [:description :identity :display-name])]
|
||||
[react/text (get-in community [:description :identity :description])]]]
|
||||
name]
|
||||
[react/text description]]]
|
||||
[react/view {:border-width 1
|
||||
:padding-vertical 8
|
||||
:border-bottom-left-radius 10
|
||||
:border-bottom-right-radius 10
|
||||
:border-color colors/gray-lighter}
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [(if joined ::communities/leave ::communities/join) (:id community)])}
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :community {:community-id (:id community)}])}
|
||||
[react/text {:style {:text-align :center
|
||||
:color colors/blue}} (if joined (i18n/label :t/leave) (i18n/label :t/join))]]]])))
|
||||
:color colors/blue}} (i18n/label :t/view)]]]])))
|
||||
|
||||
(defn message-content-wrapper
|
||||
"Author, userpic and delivery wrapper"
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
(ns status-im.ui.screens.communities.community
|
||||
(:require [status-im.ui.components.topbar :as topbar]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.core :as quo]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.screens.home.views.inner-item :as inner-item]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.utils.core :as utils]))
|
||||
|
||||
(def request-cooldown-ms (* 60 1000))
|
||||
|
||||
(defn can-request-access-again? [requested-at]
|
||||
(> (datetime/timestamp) (+ (* requested-at 1000) request-cooldown-ms)))
|
||||
|
||||
(defn toolbar-content [id display-name color images show-members-count? members]
|
||||
(let [thumbnail-image (get-in images [:thumbnail :uri])]
|
||||
[rn/view {:style {:flex 1
|
||||
:align-items :center
|
||||
:flex-direction :row}}
|
||||
[rn/view {:padding-right 10}
|
||||
(cond
|
||||
(= id constants/status-community-id)
|
||||
[rn/image {:source (resources/get-image :status-logo)
|
||||
:style {:width 40
|
||||
:height 40}}]
|
||||
(seq thumbnail-image)
|
||||
[photos/photo thumbnail-image {:size 40}]
|
||||
|
||||
:else
|
||||
[chat-icon.screen/chat-icon-view-toolbar
|
||||
id
|
||||
true
|
||||
display-name
|
||||
(or color (rand-nth colors/chat-colors))])]
|
||||
[rn/view {:style {:flex 1 :justify-content :center}}
|
||||
[quo/text {:number-of-lines 1
|
||||
:accessibility-label :community-name-text}
|
||||
display-name]
|
||||
(when show-members-count?
|
||||
[quo/text {:number-of-lines 1
|
||||
:size :small
|
||||
:color :secondary}
|
||||
(i18n/label-pluralize members :t/community-members {:count members})])]]))
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
|
||||
(defn community-actions [{:keys [id
|
||||
permissions
|
||||
can-manage-users? name images color]}]
|
||||
(let [can-invite? (and can-manage-users? (not= (:access permissions) constants/community-no-membership-access))
|
||||
can-share? (not= (:access permissions) constants/community-invitation-only-access)
|
||||
thumbnail-image (get-in images [:thumbnail :uri])]
|
||||
[:<>
|
||||
[quo/list-item
|
||||
{:title name
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :community-management {:community-id id}])
|
||||
:chevron true
|
||||
:icon (cond
|
||||
(= id constants/status-community-id)
|
||||
[rn/image {:source (resources/get-image :status-logo)
|
||||
:style {:width 40
|
||||
:height 40}}]
|
||||
(seq thumbnail-image)
|
||||
[photos/photo thumbnail-image {:size 40}]
|
||||
|
||||
:else
|
||||
[chat-icon.screen/chat-icon-view-chat-sheet
|
||||
name
|
||||
true
|
||||
name
|
||||
(or color (rand-nth colors/chat-colors))])}]
|
||||
(when (and config/communities-management-enabled? can-manage-users?)
|
||||
[:<>
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/export-key)
|
||||
:accessibility-label :community-export-key
|
||||
:icon :main-icons/objects
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/export-pressed id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/create-channel)
|
||||
:accessibility-label :community-create-channel
|
||||
:icon :main-icons/channel
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/create-channel-pressed id])}]])
|
||||
(when can-invite?
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/invite-people)
|
||||
:icon :main-icons/share
|
||||
:accessibility-label :community-invite-people
|
||||
:on-press #(>evt [::communities/invite-people-pressed id])}])
|
||||
(when can-share?
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/share)
|
||||
:icon :main-icons/share
|
||||
:accessibility-label :community-share
|
||||
:on-press #(>evt [::communities/share-community-pressed id])}])
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/leave-community)
|
||||
:accessibility-label :leave
|
||||
:icon :main-icons/arrow-left
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt [:navigate-to :home])
|
||||
(>evt [::communities/leave id]))}]]))
|
||||
|
||||
(defn welcome-blank-page []
|
||||
[rn/view {:style {:padding 16 :flex 1 :flex-direction :row :align-items :center :justify-content :center}}
|
||||
[quo/text {:align :center
|
||||
:color :secondary}
|
||||
(i18n/label :t/welcome-community-blank-message)]])
|
||||
|
||||
(defn community-chat-item [home-item]
|
||||
[inner-item/home-list-item home-item])
|
||||
|
||||
(defn community-chat-list [chats]
|
||||
(if (empty? chats)
|
||||
[welcome-blank-page]
|
||||
[list/flat-list
|
||||
{:key-fn :chat-id
|
||||
:content-container-style {:padding-vertical 8}
|
||||
:keyboard-should-persist-taps :always
|
||||
:data chats
|
||||
:render-fn community-chat-item
|
||||
:footer [rn/view {:height 68}]}]))
|
||||
|
||||
(defn community-channel-list [id]
|
||||
(let [chats (<sub [:chats/by-community-id id])
|
||||
chats (cond->> chats
|
||||
(= id constants/status-community-id)
|
||||
(map #(assoc % :color colors/blue)))]
|
||||
[community-chat-list chats]))
|
||||
|
||||
(defn channel-preview-item [{:keys [id color name]}]
|
||||
(let [color (or color (rand-nth colors/chat-colors))]
|
||||
[quo/list-item
|
||||
{:icon [chat-icon.screen/chat-icon-view-chat-list
|
||||
id true name color false false]
|
||||
:title [rn/view {:flex-direction :row
|
||||
:flex 1
|
||||
:padding-right 16
|
||||
:align-items :center}
|
||||
[icons/icon :main-icons/tiny-group
|
||||
{:color colors/black
|
||||
:width 15
|
||||
:height 15
|
||||
:container-style {:width 15
|
||||
:height 15
|
||||
:margin-right 2}}]
|
||||
[quo/text {:weight :medium
|
||||
:accessibility-label :chat-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1}
|
||||
(utils/truncate-str name 30)]]
|
||||
:title-accessibility-label :chat-name-text}]))
|
||||
|
||||
(defn community-channel-preview-list [_ chats-without-id]
|
||||
(let [chats (reduce-kv
|
||||
(fn [acc k v]
|
||||
(conj acc (assoc v :id (name k))))
|
||||
[]
|
||||
chats-without-id)]
|
||||
[list/flat-list
|
||||
{:key-fn :id
|
||||
:content-container-style {:padding-vertical 8}
|
||||
:keyboard-should-persist-taps :always
|
||||
:data chats
|
||||
:render-fn channel-preview-item}]))
|
||||
|
||||
(defn community [route]
|
||||
(let [{:keys [community-id]} (get-in route [:route :params])
|
||||
{:keys [id
|
||||
chats
|
||||
name
|
||||
images
|
||||
members
|
||||
permissions
|
||||
color
|
||||
joined
|
||||
can-request-access?
|
||||
can-join?
|
||||
requested-to-join-at
|
||||
admin]
|
||||
:as community} (<sub [:communities/community community-id])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
|
||||
[topbar/topbar
|
||||
{:content [toolbar-content id
|
||||
name
|
||||
color
|
||||
images
|
||||
(not= (:access permissions) constants/community-no-membership-access)
|
||||
(count members)]
|
||||
:right-accessories (when (or admin joined)
|
||||
[{:icon :main-icons/more
|
||||
:accessibility-label :community-menu-button
|
||||
:on-press
|
||||
#(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[community-actions community])
|
||||
:height 256}])}])}]
|
||||
(if joined
|
||||
[community-channel-list id]
|
||||
[community-channel-preview-list id chats])
|
||||
(when-not joined
|
||||
(cond
|
||||
can-join?
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:on-press #(>evt [::communities/join id])
|
||||
:type :secondary}
|
||||
(i18n/label :t/join)]}]
|
||||
can-request-access?
|
||||
(if (and (pos? requested-to-join-at)
|
||||
(not (can-request-access-again? requested-to-join-at)))
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:left [quo/text {:color :secondary} (i18n/label :t/membership-request-pending)]}]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:on-press #(>evt [::communities/request-to-join id])
|
||||
:type :secondary}
|
||||
(i18n/label :t/request-access)]}])
|
||||
:else
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:on-press #(>evt [::communities/join id])
|
||||
:type :secondary}
|
||||
(i18n/label :t/follow)]}]))]))
|
|
@ -0,0 +1,170 @@
|
|||
(ns status-im.ui.screens.communities.create
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[quo.core :as quo]
|
||||
[clojure.string :as str]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.image :as utils.image]
|
||||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.communities.membership :as memberships]
|
||||
[status-im.ui.components.icons.icons :as icons]))
|
||||
|
||||
(def max-name-length 30)
|
||||
(def max-description-length 140)
|
||||
|
||||
(defn valid? [community-name community-description]
|
||||
(and (not (str/blank? community-name))
|
||||
(not (str/blank? community-description))
|
||||
(<= (count community-name) max-name-length)
|
||||
(<= (count community-description) max-description-length)))
|
||||
|
||||
(def crop-size 1000)
|
||||
(def crop-opts {:cropping true
|
||||
:cropperCircleOverlay true
|
||||
:width crop-size
|
||||
:height crop-size})
|
||||
|
||||
(defn pick-pic []
|
||||
(react/show-image-picker
|
||||
#(>evt [::communities/create-field :image (.-path ^js %)])
|
||||
crop-opts))
|
||||
|
||||
(defn take-pic []
|
||||
(react/show-image-picker-camera
|
||||
#(>evt [::communities/create-field :image (.-path ^js %)])
|
||||
crop-opts))
|
||||
|
||||
(defn bottom-sheet [has-picture]
|
||||
(fn []
|
||||
[:<>
|
||||
[quo/list-item {:accessibility-label :take-photo
|
||||
:theme :accent
|
||||
:icon :main-icons/camera
|
||||
:title (i18n/label :t/community-image-take)
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(take-pic))}]
|
||||
[quo/list-item {:accessibility-label :pick-photo
|
||||
:icon :main-icons/gallery
|
||||
:theme :accent
|
||||
:title (i18n/label :t/community-image-pick)
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(pick-pic))}]
|
||||
(when has-picture
|
||||
[quo/list-item {:accessibility-label :remove-photo
|
||||
:icon :main-icons/delete
|
||||
:theme :accent
|
||||
:title (i18n/label :t/community-image-remove)
|
||||
:on-press #(do
|
||||
(>evt [:bottom-sheet/hide]))}])]))
|
||||
|
||||
(defn photo-picker []
|
||||
(let [{:keys [image]} (<sub [:communities/create])]
|
||||
[rn/view {:style {:padding-top 16
|
||||
:align-items :center}}
|
||||
[rn/touchable-opacity {:on-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (bottom-sheet (boolean image))}])}
|
||||
[rn/view {:style {:width 128
|
||||
:height 128}}
|
||||
[rn/view {:style {:flex 1
|
||||
:border-radius 64
|
||||
:background-color (colors/get-color :ui-01)
|
||||
:justify-content :center
|
||||
:align-items :center}}
|
||||
(if image
|
||||
[rn/image {:source (utils.image/source image)
|
||||
:style {:width 128
|
||||
:height 128
|
||||
:border-radius 64}
|
||||
:resize-mode :cover
|
||||
:accessibility-label :community-image}]
|
||||
[:<>
|
||||
[icons/icon :main-icons/photo {:color (colors/get-color :icon-02)}]
|
||||
[quo/text {:color :secondary}
|
||||
(i18n/label :t/community-thumbnail-upload)]])]
|
||||
[rn/view {:style {:position :absolute
|
||||
:top 0
|
||||
:right 7}}
|
||||
[rn/view {:style {:width 40
|
||||
:height 40
|
||||
:background-color (colors/get-color :interactive-01)
|
||||
:border-radius 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:shadow-offset {:width 0 :height 1}
|
||||
:shadow-radius 6
|
||||
:shadow-opacity 1
|
||||
:shadow-color (colors/get-color :shadow-01)
|
||||
:elevation 2}}
|
||||
[icons/icon :main-icons/add {:color colors/white}]]]]]]))
|
||||
|
||||
(defn countable-label [{:keys [label value max-length]}]
|
||||
[rn/view {:style {:padding-bottom 10
|
||||
:justify-content :space-between
|
||||
:align-items :flex-end
|
||||
:flex-direction :row
|
||||
:flex-wrap :nowrap}}
|
||||
[quo/text label]
|
||||
[quo/text {:size :small
|
||||
:color (if (> (count value) max-length)
|
||||
:negative
|
||||
:secondary)}
|
||||
(str (count value) "/" max-length)]])
|
||||
|
||||
(defn form []
|
||||
(let [{:keys [name description membership]} (<sub [:communities/create])]
|
||||
[rn/scroll-view {:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}}
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[countable-label {:label (i18n/label :t/name-your-community)
|
||||
:value name
|
||||
:max-length max-name-length}]
|
||||
[quo/text-input
|
||||
{:placeholder (i18n/label :t/name-your-community-placeholder)
|
||||
:default-value name
|
||||
:on-change-text #(>evt [::communities/create-field :name %])
|
||||
:auto-focus true}]]
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[countable-label {:label (i18n/label :t/give-a-short-description-community)
|
||||
:value description
|
||||
:max-length max-description-length}]
|
||||
[quo/text-input
|
||||
{:placeholder (i18n/label :t/give-a-short-description-community)
|
||||
:multiline true
|
||||
:default-value description
|
||||
:on-change-text #(>evt [::communities/create-field :description %])}]]
|
||||
[quo/list-header {:color :main}
|
||||
(i18n/label :t/community-thumbnail-image)]
|
||||
[photo-picker]
|
||||
[:<>
|
||||
[quo/separator {:style {:margin-vertical 10}}]
|
||||
[quo/list-item {:title (i18n/label :t/membership-button)
|
||||
:accessory-text (i18n/label (get-in memberships/options [membership :title] :t/membership-none))
|
||||
:accessory :text
|
||||
:on-press #(>evt [:navigate-to :community-membership])
|
||||
:chevron true
|
||||
:size :small}]
|
||||
[quo/list-footer
|
||||
(i18n/label (get-in memberships/options [membership :description] :t/membership-none-placeholder))]]]))
|
||||
|
||||
(defn view []
|
||||
(let [{:keys [name description]} (<sub [:communities/create])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[topbar/topbar {:title (i18n/label :t/new-community-title)}]
|
||||
[form]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(>evt [::communities/create-confirmation-pressed])}
|
||||
(i18n/label :t/create)]}]]))
|
|
@ -0,0 +1,36 @@
|
|||
(ns status-im.ui.screens.communities.create-channel
|
||||
(:require [clojure.string :as str]
|
||||
[reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.topbar :as topbar]))
|
||||
|
||||
(defn valid? [community-name]
|
||||
(not (str/blank? community-name)))
|
||||
|
||||
(defn create-channel []
|
||||
(let [channel-name (reagent/atom "")]
|
||||
(fn []
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label :t/create-channel-title)}]
|
||||
[rn/scroll-view {:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}}
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10
|
||||
:padding-horizontal 16}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/name-your-channel)
|
||||
:placeholder (i18n/label :t/name-your-channel-placeholder)
|
||||
:on-change-text #(reset! channel-name %)
|
||||
:auto-focus true}]]]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (valid? @channel-name))
|
||||
:type :secondary
|
||||
:on-press #(>evt [::communities/create-channel-confirmation-pressed @channel-name])}
|
||||
(i18n/label :t/create)]}]])))
|
|
@ -0,0 +1,21 @@
|
|||
(ns status-im.ui.screens.communities.edit
|
||||
(:require [status-im.ui.components.topbar :as topbar]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.ui.screens.communities.create :as community.create]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.toolbar :as toolbar]))
|
||||
|
||||
(defn edit []
|
||||
(let [{:keys [name description]} (<sub [:communities/create])]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label :t/community-edit-title)}]
|
||||
[community.create/form]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (not (community.create/valid? name description))
|
||||
:type :secondary
|
||||
:on-press #(>evt [::communities/edit-confirmation-pressed])}
|
||||
(i18n/label :t/save)]}]]))
|
|
@ -0,0 +1,32 @@
|
|||
(ns status-im.ui.screens.communities.import
|
||||
(:require [quo.react-native :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.handlers :refer [>evt]]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.toolbar :as toolbar]))
|
||||
|
||||
(defn view []
|
||||
(let [community-key (reagent/atom "")]
|
||||
(fn []
|
||||
[rn/view {:style {:flex 1}}
|
||||
[topbar/topbar {:title (i18n/label :t/import-community-title)}]
|
||||
[rn/scroll-view {:style {:flex 1}
|
||||
:content-container-style {:padding 16}}
|
||||
[rn/view {:style {:padding-bottom 16
|
||||
:padding-top 10}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/community-key)
|
||||
:placeholder (i18n/label :t/community-key-placeholder)
|
||||
:on-change-text #(reset! community-key %)
|
||||
:default-value @community-key
|
||||
:auto-focus true}]]]
|
||||
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:disabled (= @community-key "")
|
||||
:type :secondary
|
||||
:on-press #(>evt [::communities/import @community-key])}
|
||||
(i18n/label :t/import)]}]])))
|
|
@ -0,0 +1,79 @@
|
|||
(ns status-im.ui.screens.communities.invite
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[clojure.string :as str]))
|
||||
|
||||
(defn header [user-pk]
|
||||
[:<>
|
||||
[rn/view {:style {:padding-horizontal 16
|
||||
:padding-vertical 8}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/enter-user-pk)
|
||||
:placeholder (i18n/label :t/enter-user-pk)
|
||||
:on-change-text #(reset! user-pk %)
|
||||
:default-value @user-pk
|
||||
:auto-focus true}]]
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
[quo/list-header (i18n/label :t/contacts)]])
|
||||
|
||||
(defn contacts-list-item [{:keys [public-key active] :as contact} _ _ {:keys [selected]}]
|
||||
(let [[first-name second-name] (multiaccounts/contact-two-names contact true)]
|
||||
[quo/list-item
|
||||
{:title first-name
|
||||
:subtitle second-name
|
||||
:icon [chat-icon.screen/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:accessory :checkbox
|
||||
:active active
|
||||
:on-press (fn []
|
||||
(if active
|
||||
(swap! selected disj public-key)
|
||||
(swap! selected conj public-key)))}]))
|
||||
|
||||
(defn invite []
|
||||
(let [user-pk (reagent/atom "")
|
||||
contacts-selected (reagent/atom #{})]
|
||||
(fn [route]
|
||||
(let [contacts-data (<sub [:contacts/active])
|
||||
invite? (get-in route [:route :params :invite?])
|
||||
{:keys [permissions
|
||||
can-manage-users?]} (<sub [:communities/edited-community])
|
||||
selected @contacts-selected
|
||||
contacts (map (fn [{:keys [public-key] :as contact}]
|
||||
(assoc contact :active (contains? selected public-key)))
|
||||
contacts-data)
|
||||
;; no-membership communities can only be shared
|
||||
can-invite? (and can-manage-users?
|
||||
invite?
|
||||
(not= (:access permissions) constants/community-no-membership-access))]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label (if can-invite?
|
||||
:t/community-invite-title
|
||||
:t/community-share-title))}]
|
||||
[rn/flat-list {:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 16}
|
||||
:header [header user-pk]
|
||||
:render-data {:selected contacts-selected}
|
||||
:render-fn contacts-list-item
|
||||
:key-fn (fn [{:keys [active public-key]}]
|
||||
(str public-key active))
|
||||
:data contacts}]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center
|
||||
[quo/button {:disabled (and (str/blank? @user-pk)
|
||||
(zero? (count selected)))
|
||||
:type :secondary
|
||||
:on-press #(>evt [(if can-invite?
|
||||
::communities/invite-people-confirmation-pressed
|
||||
::communities/share-community-confirmation-pressed) @user-pk selected])}
|
||||
(i18n/label (if can-invite? :t/invite :t/share))]}]]))))
|
|
@ -0,0 +1,113 @@
|
|||
(ns status-im.ui.screens.communities.members
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.communities.core :as communities]))
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
|
||||
(defn member-sheet [{:keys [public-key] :as member} community-id can-kick-users?]
|
||||
[:<>
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]
|
||||
:title (multiaccounts/displayed-name member)
|
||||
:subtitle (i18n/label :t/view-profile)
|
||||
:accessibility-label :view-chat-details-button
|
||||
:chevron true
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/show-profile public-key])}]
|
||||
(when can-kick-users?
|
||||
[:<>
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
[quo/list-item {:theme :negative
|
||||
:icon :main-icons/arrow-left
|
||||
:title (i18n/label :t/member-kick)
|
||||
:on-press #(>evt [::communities/member-kick community-id public-key])}]
|
||||
; ban not implemented
|
||||
#_[quo/list-item {:theme :negative
|
||||
:icon :main-icons/cancel
|
||||
:title (i18n/label :t/member-ban)
|
||||
:on-press #(>evt [::communities/member-ban community-id public-key])}]])])
|
||||
|
||||
(defn render-member [public-key _ _ {:keys [community-id
|
||||
my-public-key
|
||||
can-kick-users?]}]
|
||||
(let [{:keys [nickname] :as member} (or (<sub [:contacts/contact-by-identity public-key])
|
||||
{:public-key public-key})]
|
||||
[quo/list-item
|
||||
{:title (if (seq nickname)
|
||||
nickname
|
||||
(multiaccounts/displayed-name member))
|
||||
:accessibility-label :member-item
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]
|
||||
:accessory (when (not= public-key my-public-key)
|
||||
[quo/button {:on-press #(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn [] [member-sheet member community-id can-kick-users?])}])
|
||||
:type :icon
|
||||
:theme :icon
|
||||
:accessibility-label :menu-option}
|
||||
:main-icons/more])}]))
|
||||
|
||||
(defn header [community-id]
|
||||
[:<>
|
||||
[quo/list-item {:icon :main-icons/share
|
||||
:title (i18n/label :t/invite-people)
|
||||
:accessibility-label :community-invite-people
|
||||
:theme :accent
|
||||
:on-press #(>evt [::communities/invite-people-pressed community-id])}]
|
||||
[quo/separator {:style {:margin-vertical 8}}]])
|
||||
|
||||
(defn requests-to-join [community-id]
|
||||
(let [requests (<sub [:communities/requests-to-join-for-community community-id])
|
||||
requests-count (count requests)]
|
||||
[:<>
|
||||
[quo/list-item {:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? requests-count)
|
||||
[unviewed-indicator/unviewed-indicator requests-count])]
|
||||
:on-press #(>evt [:navigate-to :community-requests-to-join {:community-id community-id}])
|
||||
:title (i18n/label :t/membership-requests)}]
|
||||
[quo/separator {:style {:margin-vertical 8}}]]))
|
||||
|
||||
(defn members [route]
|
||||
(let [{:keys [community-id]} (get-in route [:route :params])
|
||||
my-public-key (<sub [:multiaccount/public-key])
|
||||
{:keys [members
|
||||
permissions
|
||||
can-manage-users?]} (<sub [:communities/community community-id])]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label :t/community-members-title)
|
||||
|
||||
:subtitle (str (count members))}]
|
||||
[header community-id]
|
||||
(when (and can-manage-users? (= constants/community-on-request-access (:access permissions)))
|
||||
[requests-to-join community-id])
|
||||
[rn/flat-list {:data (keys members)
|
||||
:render-data {:community-id community-id
|
||||
:my-public-key my-public-key
|
||||
:can-kick-users? (and can-manage-users?
|
||||
(not= (:access permissions)
|
||||
constants/community-no-membership-access))
|
||||
:can-manage-users? can-manage-users?}
|
||||
:key-fn identity
|
||||
:render-fn render-member}]]))
|
||||
|
||||
(defn members-container [route]
|
||||
(reagent/create-class
|
||||
{:display-name "community-members-view"
|
||||
:component-did-mount (fn []
|
||||
(communities/fetch-requests-to-join! (get-in route [:route :params :community-id])))
|
||||
:reagent-render members}))
|
|
@ -0,0 +1,50 @@
|
|||
(ns status-im.ui.screens.communities.membership
|
||||
(:require [quo.react-native :as rn]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(def options {constants/community-on-request-access
|
||||
{:title :t/membership-approval
|
||||
:description :t/membership-approval-description}
|
||||
constants/community-invitation-only-access
|
||||
{:title :t/membership-invite
|
||||
:description :t/membership-invite-description}
|
||||
; disabled for now
|
||||
; constants/community-rule-ens-only
|
||||
; {:title :t/membership-ens
|
||||
; :description :t/membership-ens-description}
|
||||
constants/community-no-membership-access
|
||||
{:title :t/membership-free
|
||||
:description :t/membership-free-description}})
|
||||
|
||||
(defn option [{:keys [title description]} {:keys [selected on-select]}]
|
||||
[:<>
|
||||
[quo/list-item {:title (i18n/label title)
|
||||
:size :small
|
||||
:accessory :radio
|
||||
:active selected
|
||||
:on-press on-select}]
|
||||
[quo/list-footer
|
||||
(i18n/label description)]
|
||||
[quo/separator {:style {:margin-vertical 8}}]])
|
||||
|
||||
(defn membership []
|
||||
(let [{:keys [membership]} (<sub [:communities/create])]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label :t/membership-title)}]
|
||||
[rn/scroll-view {}
|
||||
(doall
|
||||
(for [[id o] options]
|
||||
^{:key (str "option-" id)}
|
||||
[option o {:selected (= id membership)
|
||||
:on-select #(>evt [::communities/create-field :membership id])}]))]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:type :secondary
|
||||
:on-press #(>evt [:navigate-back])}
|
||||
(i18n/label :t/done)]}]]))
|
|
@ -0,0 +1,96 @@
|
|||
(ns status-im.ui.screens.communities.profile
|
||||
(:require [quo.core :as quo]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.ui.components.profile-header.view :as profile-header]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.ui.components.unviewed-indicator :as unviewed-indicator]
|
||||
[quo.react-native :as rn]))
|
||||
|
||||
(defn management [route]
|
||||
(let [{:keys [community-id]} (get-in route [:route :params])
|
||||
requests-to-join (<sub [:communities/requests-to-join-for-community community-id])
|
||||
community (<sub [:communities/community community-id])
|
||||
|
||||
{:keys [color
|
||||
members
|
||||
permissions
|
||||
description
|
||||
name admin]} community
|
||||
roles false
|
||||
notifications false
|
||||
show-members-count? (not= (:access permissions) constants/community-no-membership-access)
|
||||
members-count (count members)]
|
||||
|
||||
[:<>
|
||||
[quo/animated-header {:left-accessories [{:icon :main-icons/arrow-left
|
||||
:accessibility-label :back-button
|
||||
:on-press #(>evt [:navigate-back])}]
|
||||
:right-accessories [{:icon :main-icons/share
|
||||
:accessibility-label :invite-button
|
||||
:on-press #(>evt [::communities/share-community-pressed community-id])}]
|
||||
:extended-header (profile-header/extended-header
|
||||
{:title name
|
||||
:color (or color (rand-nth colors/chat-colors))
|
||||
:photo (if (= community-id constants/status-community-id)
|
||||
(:uri
|
||||
(rn/resolve-asset-source
|
||||
(resources/get-image :status-logo)))
|
||||
(get-in community [:images :large :uri]))
|
||||
:subtitle (when show-members-count? (i18n/label-pluralize members-count :t/community-members {:count members-count}))})
|
||||
:use-insets true}
|
||||
[:<>
|
||||
[quo/list-footer {:color :main}
|
||||
(get-in description [:identity :description])]
|
||||
[quo/separator {:style {:margin-vertical 8}}]
|
||||
(when show-members-count?
|
||||
[quo/list-item {:chevron true
|
||||
:accessory
|
||||
[react/view {:flex-direction :row}
|
||||
(when (pos? members-count)
|
||||
[quo/text {:color :secondary} (str members-count)])
|
||||
[unviewed-indicator/unviewed-indicator (count requests-to-join)]]
|
||||
:on-press #(>evt [:navigate-to :community-members {:community-id community-id}])
|
||||
:title (i18n/label :t/members-label)
|
||||
:icon :main-icons/group-chat}])
|
||||
(when (and admin roles)
|
||||
[quo/list-item {:chevron true
|
||||
:title (i18n/label :t/commonuity-role)
|
||||
:icon :main-icons/objects}])
|
||||
(when notifications
|
||||
[quo/list-item {:chevron true
|
||||
:title (i18n/label :t/chat-notification-preferences)
|
||||
:icon :main-icons/notification}])
|
||||
(when (or show-members-count? notifications (and admin roles))
|
||||
[quo/separator {:style {:margin-vertical 8}}])
|
||||
;; Disable as not implemented yet
|
||||
(when false
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/edit
|
||||
:title (i18n/label :t/edit-community)
|
||||
:on-press #(>evt [::communities/open-edit-community community-id])}])
|
||||
[quo/list-item {:theme :accent
|
||||
:icon :main-icons/arrow-left
|
||||
:title (i18n/label :t/leave-community)
|
||||
:on-press #(>evt [::communities/leave community-id])}]
|
||||
;; Disable as not implemented yet
|
||||
(when false
|
||||
[quo/list-item {:theme :negative
|
||||
:icon :main-icons/delete
|
||||
:title (i18n/label :t/delete)
|
||||
:on-press #(>evt [::communities/delete-community community-id])}])]]]))
|
||||
|
||||
(defn management-container [route]
|
||||
(reagent/create-class
|
||||
{:display-name "community-profile-view"
|
||||
:component-did-mount (fn []
|
||||
(communities/fetch-requests-to-join! (get-in route [:route :params :community-id])))
|
||||
:reagent-render management}))
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
(ns status-im.ui.screens.communities.requests-to-join
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
[reagent.core :as reagent]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.handlers :refer [<sub >evt]]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.communities.core :as communities]))
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
|
||||
(defn request-actions [community-id request-id]
|
||||
[react/view {:flex-direction :row}
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:communities.ui/accept-request-to-join-pressed community-id request-id])}
|
||||
[icons/icon :main-icons/checkmark-circle {:width 35
|
||||
:height 35
|
||||
:color colors/green}]]
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:communities.ui/decline-request-to-join-pressed community-id request-id])}
|
||||
[icons/icon :main-icons/cancel {:width 35
|
||||
:height 35
|
||||
:container-style {:padding-left 10}
|
||||
:color colors/red}]]])
|
||||
|
||||
(defn render-request [{:keys [id public-key]} _ _ {:keys [community-id
|
||||
can-manage-users?]}]
|
||||
(let [member (or (<sub [:contacts/contact-by-identity public-key])
|
||||
{:public-key public-key})]
|
||||
[quo/list-item
|
||||
{:title (multiaccounts/displayed-name member)
|
||||
:accessibility-label :member-item
|
||||
|
||||
:accessory (when can-manage-users?
|
||||
[request-actions community-id id])
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo member)]}]))
|
||||
|
||||
(defn requests-to-join [route]
|
||||
(fn []
|
||||
(let [{:keys [community-id]} (get-in route [:route :params])
|
||||
requests (<sub [:communities/requests-to-join-for-community community-id])
|
||||
{:keys [can-manage-users?]} (<sub [:communities/community community-id])]
|
||||
[:<>
|
||||
[topbar/topbar {:title (i18n/label :t/community-requests-to-join-title)
|
||||
:subtitle (str (count requests))}]
|
||||
[rn/flat-list {:data requests
|
||||
:render-data {:community-id community-id
|
||||
:can-manage-users? can-manage-users?}
|
||||
:key-fn :id
|
||||
:render-fn render-request}]])))
|
||||
|
||||
(defn requests-to-join-container [route]
|
||||
(reagent/create-class
|
||||
{:display-name "community-requests-to-join-view"
|
||||
:component-did-mount (fn []
|
||||
(communities/fetch-requests-to-join! (get-in route [:route :params :community-id])))
|
||||
:reagent-render requests-to-join}))
|
|
@ -1,304 +1,55 @@
|
|||
(ns status-im.ui.screens.communities.views (:require-macros [status-im.utils.views :as views])
|
||||
(ns status-im.ui.screens.communities.views
|
||||
(:require
|
||||
[reagent.core :as reagent]
|
||||
[re-frame.core :as re-frame]
|
||||
[quo.core :as quo]
|
||||
[status-im.i18n.i18n :as i18n]
|
||||
[status-im.utils.core :as utils]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.communities.core :as communities]
|
||||
[status-im.ui.screens.home.views.inner-item :as inner-item]
|
||||
[status-im.ui.screens.home.styles :as home.styles]
|
||||
[status-im.utils.handlers :refer [>evt <sub]]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.ui.components.list.views :as list]
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
[status-im.react-native.resources :as resources]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.icons.icons :as icons]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.react :as react]))
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch event))
|
||||
(>evt [:bottom-sheet/hide])
|
||||
(>evt event))
|
||||
|
||||
(defn community-list-item [{:keys [id description]}]
|
||||
(let [identity (:identity description)]
|
||||
[quo/list-item
|
||||
{:icon (if (= id constants/status-community-id)
|
||||
[react/image {:source (resources/get-image :status-logo)
|
||||
:style {:width 40
|
||||
:height 40}}]
|
||||
|
||||
[chat-icon.screen/chat-icon-view-chat-list
|
||||
id
|
||||
true
|
||||
(:display-name identity)
|
||||
;; TODO: should be derived by id
|
||||
(or (:color identity)
|
||||
(rand-nth colors/chat-colors))
|
||||
false
|
||||
false])
|
||||
:title [react/view {:flex-direction :row
|
||||
:flex 1}
|
||||
[react/view {:flex-direction :row
|
||||
:flex 1
|
||||
:padding-right 16
|
||||
:align-items :center}
|
||||
[quo/text {:weight :medium
|
||||
:accessibility-label :community-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1}
|
||||
(utils/truncate-str (:display-name identity) 30)]]]
|
||||
:title-accessibility-label :community-name-text
|
||||
:subtitle [react/view {:flex-direction :row}
|
||||
[react/view {:flex 1}
|
||||
[quo/text
|
||||
(utils/truncate-str (:description identity) 30)]]]
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(re-frame/dispatch [:navigate-to :community id]))}]))
|
||||
|
||||
(defn communities-actions []
|
||||
[react/view
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/import-community)
|
||||
:accessibility-label :community-import-community
|
||||
:icon :main-icons/check
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/import-pressed])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/create-community)
|
||||
:accessibility-label :community-create-community
|
||||
:icon :main-icons/check
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/create-pressed])}]])
|
||||
|
||||
(views/defview communities []
|
||||
(views/letsubs [communities [:communities]]
|
||||
[react/view {:flex 1}
|
||||
[topbar/topbar (cond-> {:title (i18n/label :t/communities)}
|
||||
config/communities-management-enabled?
|
||||
(assoc :right-accessories [{:icon :main-icons/more
|
||||
:accessibility-label :chat-menu-button
|
||||
:on-press
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[communities-actions])
|
||||
:height 256}])}]))]
|
||||
[react/scroll-view {:style {:flex 1}
|
||||
:content-container-style {:padding-vertical 8}}
|
||||
[list/flat-list
|
||||
{:key-fn :id
|
||||
:keyboard-should-persist-taps :always
|
||||
:data (vals communities)
|
||||
:render-fn (fn [community] [community-list-item community])}]]
|
||||
(when config/communities-management-enabled?
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:on-press #(re-frame/dispatch [::communities/create-pressed])}
|
||||
(i18n/label :t/create)]}])]))
|
||||
|
||||
(defn valid? [community-name community-description]
|
||||
(and (not= "" community-name)
|
||||
(not= "" community-description)))
|
||||
|
||||
(defn import-community []
|
||||
(let [community-key (reagent/atom "")]
|
||||
(fn []
|
||||
[react/view {:style {:padding-left 16
|
||||
:padding-right 8}}
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/community-key)
|
||||
:placeholder (i18n/label :t/community-key-placeholder)
|
||||
:on-change-text #(reset! community-key %)
|
||||
:auto-focus true}]]
|
||||
[react/view {:style {:padding-top 20
|
||||
:padding-horizontal 20}}
|
||||
[quo/button {:disabled (= @community-key "")
|
||||
:on-press #(re-frame/dispatch [::communities/import-confirmation-pressed @community-key])}
|
||||
(i18n/label :t/import)]]])))
|
||||
|
||||
(defn create []
|
||||
(let [community-name (reagent/atom "")
|
||||
membership (reagent/atom 1)
|
||||
community-description (reagent/atom "")]
|
||||
(fn []
|
||||
[react/view {:style {:padding-left 16
|
||||
:padding-right 8}}
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/name-your-community)
|
||||
:placeholder (i18n/label :t/name-your-community-placeholder)
|
||||
:on-change-text #(reset! community-name %)
|
||||
:auto-focus true}]]
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/give-a-short-description-community)
|
||||
:placeholder (i18n/label :t/give-a-short-description-community)
|
||||
:multiline true
|
||||
:number-of-lines 4
|
||||
:on-change-text #(reset! community-description %)}]]
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/membership-type)
|
||||
:placeholder (i18n/label :t/membership-type-placeholder)
|
||||
:on-change-text #(reset! membership %)}]]
|
||||
|
||||
[react/view {:style {:padding-top 20
|
||||
:padding-horizontal 20}}
|
||||
[quo/button {:disabled (not (valid? @community-name @community-description))
|
||||
:on-press #(re-frame/dispatch [::communities/create-confirmation-pressed @community-name @community-description @membership])}
|
||||
(i18n/label :t/create)]]])))
|
||||
|
||||
(def create-sheet
|
||||
{:content create})
|
||||
|
||||
(def import-sheet
|
||||
{:content import-community})
|
||||
|
||||
(defn create-channel []
|
||||
(let [channel-name (reagent/atom "")
|
||||
channel-description (reagent/atom "")]
|
||||
(fn []
|
||||
[react/view {:style {:padding-left 16
|
||||
:padding-right 8}}
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/name-your-channel)
|
||||
:placeholder (i18n/label :t/name-your-channel-placeholder)
|
||||
:on-change-text #(reset! channel-name %)
|
||||
:auto-focus true}]]
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/give-a-short-description-channel)
|
||||
:placeholder (i18n/label :t/give-a-short-description-channel)
|
||||
:multiline true
|
||||
:number-of-lines 4
|
||||
:on-change-text #(reset! channel-description %)}]]
|
||||
|
||||
(when config/communities-management-enabled?
|
||||
[react/view {:style {:padding-top 20
|
||||
:padding-horizontal 20}}
|
||||
[quo/button {:disabled (not (valid? @channel-name @channel-description))
|
||||
:on-press #(re-frame/dispatch [::communities/create-channel-confirmation-pressed @channel-name @channel-description])}
|
||||
(i18n/label :t/create)]])])))
|
||||
|
||||
(def create-channel-sheet
|
||||
{:content create-channel})
|
||||
|
||||
(defn invite-people []
|
||||
(let [user-pk (reagent/atom "")]
|
||||
(fn []
|
||||
[react/view {:style {:padding-left 16
|
||||
:padding-right 8}}
|
||||
[react/view {:style {:padding-horizontal 20}}
|
||||
[quo/text-input
|
||||
{:label (i18n/label :t/enter-user-pk)
|
||||
:placeholder (i18n/label :t/enter-user-pk)
|
||||
:on-change-text #(reset! user-pk %)
|
||||
:auto-focus true}]]
|
||||
[react/view {:style {:padding-top 20
|
||||
:padding-horizontal 20}}
|
||||
[quo/button {:disabled (= "" user-pk)
|
||||
:on-press #(re-frame/dispatch [::communities/invite-people-confirmation-pressed @user-pk])}
|
||||
(i18n/label :t/invite)]]])))
|
||||
|
||||
(def invite-people-sheet
|
||||
{:content invite-people})
|
||||
|
||||
(defn community-actions [id admin]
|
||||
[react/view
|
||||
(when (and config/communities-management-enabled? admin)
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/export-key)
|
||||
:accessibility-label :community-export-key
|
||||
:icon :main-icons/check
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/export-pressed id])}])
|
||||
(when (and config/communities-management-enabled? admin)
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/create-channel)
|
||||
:accessibility-label :community-create-channel
|
||||
:icon :main-icons/check
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/create-channel-pressed id])}])
|
||||
(when (and config/communities-management-enabled? admin)
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/invite-people)
|
||||
:accessibility-label :community-invite-people
|
||||
:icon :main-icons/close
|
||||
:on-press #(re-frame/dispatch [::communities/invite-people-pressed id])}])
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/leave)
|
||||
:accessibility-label :leave
|
||||
:icon :main-icons/close
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:navigate-to :home])
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(re-frame/dispatch [::communities/leave id]))}]])
|
||||
|
||||
(defn toolbar-content [id display-name color]
|
||||
[react/view {:style {:flex 1
|
||||
:align-items :center
|
||||
:flex-direction :row}}
|
||||
[react/view {:margin-right 10}
|
||||
(if (= id constants/status-community-id)
|
||||
[react/image {:source (resources/get-image :status-logo)
|
||||
:style {:width 40
|
||||
:height 40}}]
|
||||
[chat-icon.screen/chat-icon-view-toolbar
|
||||
id
|
||||
true
|
||||
display-name
|
||||
(or color
|
||||
(rand-nth colors/chat-colors))])]
|
||||
[react/view {:style {:flex 1 :justify-content :center}}
|
||||
[react/text {:style {:typography :main-medium
|
||||
:font-size 15
|
||||
:line-height 22}
|
||||
:number-of-lines 1
|
||||
:accessibility-label :community-name-text}
|
||||
display-name]]])
|
||||
|
||||
(defn topbar [id display-name color admin joined]
|
||||
[topbar/topbar
|
||||
{:content [toolbar-content id display-name color]
|
||||
:navigation {:on-press #(re-frame/dispatch [:navigate-back])}
|
||||
:right-accessories (when (or admin joined)
|
||||
[{:icon :main-icons/more
|
||||
:accessibility-label :community-menu-button
|
||||
:on-press
|
||||
#(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[community-actions id admin])
|
||||
:height 256}])}])}])
|
||||
|
||||
(defn welcome-blank-page []
|
||||
[react/view {:style {:flex 1 :flex-direction :row :align-items :center :justify-content :center}}
|
||||
[react/i18n-text {:style home.styles/welcome-blank-text :key :welcome-blank-message}]])
|
||||
|
||||
(views/defview community-unviewed-count [id]
|
||||
(views/letsubs [unviewed-count [:communities/unviewed-count id]]
|
||||
(when-not (zero? unviewed-count)
|
||||
(defn community-unviewed-count [id]
|
||||
(when-not (zero? (<sub [:communities/unviewed-count id]))
|
||||
[react/view {:style {:background-color colors/blue
|
||||
:border-radius 6
|
||||
:margin-right 5
|
||||
:margin-top 2
|
||||
:width 12
|
||||
:height 12}
|
||||
:accessibility-label :unviewed-messages-public}])))
|
||||
:accessibility-label :unviewed-messages-public}]))
|
||||
|
||||
(defn status-community [{:keys [id description]}]
|
||||
[quo/list-item
|
||||
{:icon [react/image {:source (resources/get-image :status-logo)
|
||||
(defn community-icon [{:keys [id name images color]}]
|
||||
(let [color (or color (rand-nth colors/chat-colors))
|
||||
thumbnail-image (get-in images [:thumbnail :uri])]
|
||||
(cond
|
||||
(= id constants/status-community-id)
|
||||
[react/image {:source (resources/get-image :status-logo)
|
||||
:style {:width 40
|
||||
:height 40}}]
|
||||
(seq thumbnail-image)
|
||||
[photos/photo thumbnail-image {:size 40}]
|
||||
|
||||
:else
|
||||
[chat-icon.screen/chat-icon-view-chat-list
|
||||
id true name color false false])))
|
||||
|
||||
(defn community-home-list-item [{:keys [id name last?] :as community}]
|
||||
[react/view
|
||||
[quo/list-item
|
||||
{:icon [community-icon community]
|
||||
:title [react/view {:flex-direction :row
|
||||
:flex 1}
|
||||
[react/view {:flex-direction :row
|
||||
|
@ -310,114 +61,129 @@
|
|||
:font-size 17
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1}
|
||||
(get-in description [:identity :display-name])]]
|
||||
name]]
|
||||
[react/view {:flex-direction :row
|
||||
:flex 1
|
||||
:justify-content :flex-end
|
||||
:align-items :center}
|
||||
[community-unviewed-count id]]]
|
||||
|
||||
:title-accessibility-label :chat-name-text
|
||||
:on-press #(do
|
||||
(re-frame/dispatch [:dismiss-keyboard])
|
||||
(re-frame/dispatch [:navigate-to :community id]))
|
||||
(>evt [:dismiss-keyboard])
|
||||
(>evt [:navigate-to :community {:community-id id}]))
|
||||
;; TODO: actions
|
||||
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
nil])}])
|
||||
;; :on-long-press #(>evt [:bottom-sheet/show-sheet
|
||||
;; nil])
|
||||
}]
|
||||
(when last?
|
||||
[quo/separator])])
|
||||
|
||||
(defn channel-preview-item [{:keys [id identity]}]
|
||||
(defn community-list-item [{:keys [id permissions members name description] :as community}]
|
||||
(let [members-count (count members)
|
||||
show-members-count? (not= (:access permissions) constants/community-no-membership-access)]
|
||||
[quo/list-item
|
||||
{:icon [chat-icon.screen/chat-icon-view-chat-list
|
||||
id true (:display-name identity) colors/blue false false]
|
||||
{:icon [community-icon community]
|
||||
:title [react/view {:flex-direction :row
|
||||
:flex 1}
|
||||
[react/view {:flex-direction :row
|
||||
:flex 1
|
||||
:padding-right 16
|
||||
:align-items :center}
|
||||
[icons/icon :main-icons/tiny-group
|
||||
{:color colors/black
|
||||
:width 15
|
||||
:height 15
|
||||
:container-style {:width 15
|
||||
:height 15
|
||||
:margin-right 2}}]
|
||||
[quo/text {:weight :medium
|
||||
:accessibility-label :chat-name-text
|
||||
:accessibility-label :community-name-text
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1}
|
||||
(utils/truncate-str (:display-name identity) 30)]]]
|
||||
:title-accessibility-label :chat-name-text
|
||||
:subtitle [react/view {:flex-direction :row}
|
||||
[react/text-class {:style home.styles/last-message-text
|
||||
:number-of-lines 1
|
||||
:ellipsize-mode :tail
|
||||
:accessibility-label :chat-message-text} (:description identity)]]}])
|
||||
(utils/truncate-str name 30)]]
|
||||
:title-accessibility-label :community-name-text
|
||||
:subtitle [react/view
|
||||
[quo/text {:number-of-lines 1}
|
||||
description]
|
||||
[quo/text {:number-of-lines 1
|
||||
:color :secondary}
|
||||
(if show-members-count?
|
||||
(i18n/label-pluralize members-count :t/community-members {:count members-count})
|
||||
(i18n/label :t/open-membership))]]
|
||||
:on-press #(do
|
||||
(>evt [:dismiss-keyboard])
|
||||
(>evt [:navigate-to :community {:community-id id}]))}]))
|
||||
|
||||
(defn community-channel-preview-list [_ description]
|
||||
(let [chats (reduce-kv
|
||||
(fn [acc k v]
|
||||
(conj acc (assoc v :id (name k))))
|
||||
[]
|
||||
(get-in description [:chats]))]
|
||||
(defn communities-actions []
|
||||
[:<>
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/import-community)
|
||||
:accessibility-label :community-import-community
|
||||
:icon :main-icons/objects
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :community-import])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/create-community)
|
||||
:accessibility-label :community-create-community
|
||||
:icon :main-icons/add
|
||||
:on-press #(hide-sheet-and-dispatch [::communities/open-create-community])}]])
|
||||
|
||||
(defn communities-home-list [communities]
|
||||
[list/flat-list
|
||||
{:key-fn :id
|
||||
:keyboard-should-persist-taps :always
|
||||
:data chats
|
||||
:render-fn channel-preview-item}]))
|
||||
:data communities
|
||||
:render-fn community-home-list-item}])
|
||||
|
||||
(defn community-chat-list [chats]
|
||||
(if (empty? chats)
|
||||
[welcome-blank-page]
|
||||
[list/flat-list
|
||||
{:key-fn :chat-id
|
||||
(defn communities-list [communities]
|
||||
[list/section-list
|
||||
{:content-container-style {:padding-vertical 8}
|
||||
:key-fn :id
|
||||
:keyboard-should-persist-taps :always
|
||||
:data chats
|
||||
:render-fn (fn [home-item] [inner-item/home-list-item (assoc home-item :color colors/blue)])
|
||||
:footer [react/view {:height 68}]}]))
|
||||
:sticky-section-headers-enabled false
|
||||
:sections communities
|
||||
:render-section-header-fn quo/list-index
|
||||
:render-fn community-list-item}])
|
||||
|
||||
(views/defview community-channel-list [id]
|
||||
(views/letsubs [chats [:chats/by-community-id id]]
|
||||
[community-chat-list chats]))
|
||||
(defn communities []
|
||||
(let [communities (<sub [:communities/section-list])]
|
||||
[react/view {:flex 1}
|
||||
[topbar/topbar (cond-> {:title (i18n/label :t/communities)
|
||||
:modal? true}
|
||||
config/communities-management-enabled?
|
||||
(assoc :right-accessories [{:icon :main-icons/more
|
||||
:accessibility-label :chat-menu-button
|
||||
:on-press
|
||||
#(>evt [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[communities-actions])
|
||||
:height 256}])}]))]
|
||||
[communities-list communities]
|
||||
(when config/communities-management-enabled?
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:center [quo/button {:on-press #(>evt [::communities/open-create-community])
|
||||
:type :secondary}
|
||||
(i18n/label :t/create)]}])]))
|
||||
|
||||
(views/defview community [route]
|
||||
(views/letsubs [{:keys [id description joined admin]} [:communities/community (get-in route [:route :params])]]
|
||||
[react/view {:style {:flex 1}}
|
||||
[topbar
|
||||
id
|
||||
(get-in description [:identity :display-name])
|
||||
(get-in description [:identity :color])
|
||||
admin
|
||||
joined]
|
||||
(if joined
|
||||
[community-channel-list id]
|
||||
[community-channel-preview-list id description])
|
||||
(when-not joined
|
||||
[react/view {:style {:padding-top 20
|
||||
:margin-bottom 10
|
||||
:padding-horizontal 20}}
|
||||
[quo/button {:on-press #(re-frame/dispatch [::communities/join id])}
|
||||
(i18n/label :t/join)]])]))
|
||||
|
||||
(views/defview export-community []
|
||||
(views/letsubs [{:keys [community-key]} [:popover/popover]]
|
||||
[react/view {}
|
||||
[react/view {:style {:padding-top 16 :padding-horizontal 16}}
|
||||
(defn export-community []
|
||||
(let [{:keys [community-key]} (<sub [:popover/popover])]
|
||||
[react/view {:style {:padding-top 16
|
||||
:padding-horizontal 16}}
|
||||
[quo/text {:size :x-large
|
||||
:align :center}
|
||||
(i18n/label :t/community-private-key)]
|
||||
[copyable-text/copyable-text-view
|
||||
{:label :t/community-key
|
||||
:container-style {:margin-top 12 :margin-bottom 4}
|
||||
{:container-style {:padding-vertical 12}
|
||||
:copied-text community-key}
|
||||
[quo/text {:number-of-lines 1
|
||||
:ellipsize-mode :middle
|
||||
:accessibility-label :chat-key
|
||||
:monospace true}
|
||||
community-key]]]]))
|
||||
community-key]]]))
|
||||
|
||||
(defn render-featured-community [{:keys [name id]}]
|
||||
^{:key id}
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :community id])
|
||||
[react/touchable-highlight {:on-press #(>evt [:navigate-to :community {:community-id id}])
|
||||
:accessibility-label :chat-item}
|
||||
[react/view {:padding-right 8 :padding-vertical 8}
|
||||
[react/view {:border-color colors/gray-lighter :border-radius 36 :border-width 1 :padding-horizontal 8 :padding-vertical 5}
|
||||
[react/text {:style {:color colors/blue :typography :main-medium}} name]]]])
|
||||
[react/view {:padding-right 8
|
||||
:padding-vertical 8}
|
||||
[react/view {:border-color colors/gray-lighter
|
||||
:border-radius 36
|
||||
:border-width 1
|
||||
:padding-horizontal 8
|
||||
:padding-vertical 5}
|
||||
[quo/text {:color :link} name]]]])
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
:title (i18n/label :t/communities-alpha)
|
||||
:accessibility-label :communities-button
|
||||
:icon :main-icons/communities
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :communities])}])
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :communities {:screen :communities}])}])
|
||||
[invite/list-item
|
||||
{:accessibility-label :chats-menu-invite-friends-button}]])
|
||||
|
||||
|
|
|
@ -145,15 +145,17 @@
|
|||
(re-frame/dispatch [:set :public-group-topic nil])
|
||||
(re-frame/dispatch [:search/home-filter-changed nil]))}])])))
|
||||
|
||||
(defn render-fn [home-item]
|
||||
[inner-item/home-list-item home-item])
|
||||
(defn render-fn [{:keys [chat-id] :as home-item}]
|
||||
;; We use `chat-id` to distinguish communities from chats
|
||||
(if chat-id
|
||||
[inner-item/home-list-item home-item]
|
||||
[communities.views/community-home-list-item home-item]))
|
||||
|
||||
(defn communities-and-chats [chats status-community loading? search-filter hide-home-tooltip?]
|
||||
(defn communities-and-chats [items loading? search-filter hide-home-tooltip?]
|
||||
(if loading?
|
||||
[react/view {:flex 1 :align-items :center :justify-content :center}
|
||||
[react/activity-indicator {:animating true}]]
|
||||
(if (and (empty? chats)
|
||||
(not status-community)
|
||||
(if (and (empty? items)
|
||||
(empty? search-filter)
|
||||
hide-home-tooltip?
|
||||
(not @search-active?))
|
||||
|
@ -161,33 +163,26 @@
|
|||
[list/flat-list
|
||||
{:key-fn :chat-id
|
||||
:keyboard-should-persist-taps :always
|
||||
:data chats
|
||||
:data items
|
||||
:render-fn render-fn
|
||||
:header [:<>
|
||||
(when (or (seq chats) @search-active? (seq search-filter))
|
||||
[search-input-wrapper search-filter chats])
|
||||
(when (or (seq items) @search-active? (seq search-filter))
|
||||
[search-input-wrapper search-filter items])
|
||||
[referral-item/list-item]
|
||||
(when (and (empty? chats)
|
||||
(not status-community)
|
||||
(when
|
||||
(and (empty? items)
|
||||
(or @search-active? (seq search-filter)))
|
||||
[start-suggestion search-filter])
|
||||
(when status-community
|
||||
;; We only support one community now, Status
|
||||
[communities.views/status-community status-community])
|
||||
(when (and status-community
|
||||
(seq chats))
|
||||
[quo/separator])]
|
||||
[start-suggestion search-filter])]
|
||||
:footer (if (and (not hide-home-tooltip?) (not @search-active?))
|
||||
[home-tooltip-view]
|
||||
[react/view {:height 68}])}])))
|
||||
|
||||
(views/defview chats-list []
|
||||
(views/letsubs [status-community [:communities/status-community]
|
||||
loading? [:chats/loading?]
|
||||
{:keys [chats search-filter]} [:home-items]
|
||||
(views/letsubs [loading? [:chats/loading?]
|
||||
{:keys [items
|
||||
search-filter]} [:home-items]
|
||||
{:keys [hide-home-tooltip?]} [:multiaccount]]
|
||||
[react/scroll-view
|
||||
[communities-and-chats chats status-community loading? search-filter hide-home-tooltip?]]))
|
||||
[communities-and-chats items loading? search-filter hide-home-tooltip?]))
|
||||
|
||||
(views/defview plus-button []
|
||||
(views/letsubs [logging-in? [:multiaccounts/login]]
|
||||
|
|
|
@ -5,12 +5,24 @@
|
|||
[status-im.ui.screens.group.views :as group]
|
||||
[status-im.ui.screens.referrals.public-chat :as referrals.public-chat]
|
||||
[status-im.ui.screens.communities.views :as communities]
|
||||
[status-im.ui.screens.communities.community :as community]
|
||||
[status-im.ui.screens.communities.create :as communities.create]
|
||||
[status-im.ui.screens.communities.import :as communities.import]
|
||||
[status-im.ui.screens.communities.profile :as community.profile]
|
||||
[status-im.ui.screens.communities.edit :as community.edit]
|
||||
[status-im.ui.screens.communities.create-channel :as create-channel]
|
||||
[status-im.ui.screens.communities.membership :as membership]
|
||||
[status-im.ui.screens.communities.members :as members]
|
||||
[status-im.ui.screens.communities.requests-to-join :as requests-to-join]
|
||||
[status-im.ui.screens.communities.invite :as invite]
|
||||
[status-im.ui.screens.profile.group-chat.views :as profile.group-chat]
|
||||
[status-im.ui.components.tabbar.styles :as tabbar.styles]
|
||||
[status-im.ui.screens.stickers.views :as stickers]))
|
||||
[status-im.ui.screens.stickers.views :as stickers]
|
||||
[status-im.utils.config :as config]))
|
||||
|
||||
(defonce stack (navigation/create-stack))
|
||||
(defonce group-stack (navigation/create-stack))
|
||||
(defonce communities-stack (navigation/create-stack))
|
||||
|
||||
(defn chat-stack []
|
||||
[stack {:initial-route-name :home
|
||||
|
@ -20,14 +32,6 @@
|
|||
:component home/home}
|
||||
{:name :referral-enclav
|
||||
:component referrals.public-chat/view}
|
||||
{:name :communities
|
||||
:transition :presentation-ios
|
||||
:insets {:bottom true}
|
||||
:component communities/communities}
|
||||
{:name :community
|
||||
:transition :presentation-ios
|
||||
:insets {:bottom true}
|
||||
:component communities/community}
|
||||
{:name :chat
|
||||
:component chat/chat}
|
||||
{:name :group-chat-profile
|
||||
|
@ -38,7 +42,50 @@
|
|||
{:name :stickers
|
||||
:component stickers/packs}
|
||||
{:name :stickers-pack
|
||||
:component stickers/pack}]])
|
||||
:component stickers/pack}
|
||||
;; Community
|
||||
{:name :community
|
||||
:component community/community}
|
||||
{:name :community-management
|
||||
:insets {:top false}
|
||||
:component community.profile/management-container}
|
||||
{:name :community-members
|
||||
:component members/members-container}
|
||||
{:name :community-requests-to-join
|
||||
:component requests-to-join/requests-to-join-container}
|
||||
{:name :create-community-channel
|
||||
:component create-channel/create-channel}
|
||||
{:name :invite-people-community
|
||||
:component invite/invite}]])
|
||||
|
||||
(defn communities []
|
||||
[communities-stack {:header-mode :none}
|
||||
(concat
|
||||
[{:name :communities
|
||||
:insets {:bottom true
|
||||
:top false}
|
||||
:component communities/communities}
|
||||
{:name :community-import
|
||||
:insets {:bottom true
|
||||
:top false}
|
||||
:component communities.import/view}
|
||||
{:name :invite-people-community
|
||||
:insets {:bottom true
|
||||
:top false}
|
||||
:component invite/invite}]
|
||||
(when config/communities-management-enabled?
|
||||
[{:name :community-edit
|
||||
:insets {:bottom true
|
||||
:top false}
|
||||
:component community.edit/edit}
|
||||
{:name :community-create
|
||||
:insets {:bottom true
|
||||
:top false}
|
||||
:component communities.create/view}
|
||||
{:name :community-membership
|
||||
:insets {:bottom true
|
||||
:top false}
|
||||
:component membership/membership}]))])
|
||||
|
||||
(defn new-group-chat []
|
||||
[group-stack {:header-mode :none
|
||||
|
|
|
@ -100,6 +100,9 @@
|
|||
{:name :create-group-chat
|
||||
:transition :presentation-ios
|
||||
:component chat-stack/new-group-chat}
|
||||
{:name :communities
|
||||
:transition :presentation-ios
|
||||
:component chat-stack/communities}
|
||||
{:name :referral-invite
|
||||
:transition :presentation-ios
|
||||
:insets {:bottom true}
|
||||
|
|
|
@ -52,3 +52,7 @@
|
|||
name
|
||||
[debug-handlers-names (re-frame/inject-cofx :now) interceptors]
|
||||
handler)))
|
||||
|
||||
(def <sub (comp deref re-frame/subscribe))
|
||||
|
||||
(def >evt re-frame/dispatch)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.71.7",
|
||||
"commit-sha1": "99a304686faadd465a90704b1e228f62ef873df2",
|
||||
"src-sha256": "1wgwsh140bbha049adrr4jlaxzwmwd7n6a89r2wjpvdydfcpxpiv"
|
||||
"version": "v0.72.0",
|
||||
"commit-sha1": "f115b8d289684ac51dec30e5d27f57722c0a6724",
|
||||
"src-sha256": "0h94ki44by233abwg055nhm4wg7i6f8f8jmndm9813a965x51rgp"
|
||||
}
|
||||
|
|
|
@ -146,17 +146,64 @@
|
|||
"close-app-title": "Warning!",
|
||||
"command-button-send": "Send",
|
||||
"communities": "Communities",
|
||||
"community-members": {
|
||||
"one": "{{count}} member",
|
||||
"other": "{{count}} members"
|
||||
},
|
||||
"members-label": "Members",
|
||||
"open-membership": "Open membership",
|
||||
"member-kick": "Kick member",
|
||||
"membership-requests": "Membership requests",
|
||||
"community-members-title": "Members",
|
||||
"community-requests-to-join-title": "Membership requests",
|
||||
"name-your-channel": "Name your channel",
|
||||
"name-your-channel-placeholder": "Channel name",
|
||||
"give-a-short-description": "Give a short description",
|
||||
"communities-alpha": "Communities (alpha)",
|
||||
"communities-verified": "✓ Verified Status Community",
|
||||
"request-access": "Request access",
|
||||
"membership-request-pending": "Membership request pending",
|
||||
"create-community": "Create a community",
|
||||
"edit-community": "Edit community",
|
||||
"community-edit-title": "Edit community",
|
||||
"community-invite-title": "Invite",
|
||||
"community-share-title": "Share",
|
||||
"invite": "Invite",
|
||||
"create-channel": "Create a channel",
|
||||
"import-community": "Import a community",
|
||||
"import-community-title": "Import a community",
|
||||
"name-your-community": "Name your community",
|
||||
"name-your-community-placeholder": "A catchy name",
|
||||
"give-a-short-description-community": "Give it a short description",
|
||||
"new-community-title": "New community",
|
||||
"membership-title": "Membership requirement",
|
||||
"create-channel-title": "New channel",
|
||||
"community-thumbnail-image": "Thumbnail image",
|
||||
"community-thumbnail-upload": "Upload",
|
||||
"community-image-take": "Take a photo",
|
||||
"community-image-pick": "Pick an image",
|
||||
"community-image-delete": "",
|
||||
"community-color": "Community colour",
|
||||
"community-color-placeholder": "Pick a colour",
|
||||
"membership-button": "Membership requirement",
|
||||
"membership-none": "None",
|
||||
"membership-none-placeholder": "You can require new members to meet certain criteria before they can join. This can be changed at any time",
|
||||
"membership-approval": "Require approval",
|
||||
"membership-approval-description": "Your community is free to join, but new members are required to be approved by the community creator first",
|
||||
"membership-invite": "Require invite from another member",
|
||||
"membership-invite-description": "Your community can only be joined by an invitation from existing community members",
|
||||
"membership-ens": "Require ENS username",
|
||||
"membership-ens-description": "Your community requires an ENS username to be able to join",
|
||||
"membership-free": "No requirement",
|
||||
"membership-free-description": "Your community is free for anyone to join",
|
||||
"community-roles": "Roles",
|
||||
"community-key": "Community private key",
|
||||
"community-key-placeholder": "Type your community private key",
|
||||
"leave-community": "Leave community",
|
||||
"enter-user-pk": "Enter user public key",
|
||||
"import": "Import",
|
||||
"complete-hardwallet-setup": "This card is now linked. You need it to sign transactions and unlock your keys",
|
||||
"chat-notification-preferences": "Notification settings",
|
||||
"completed": "Completed",
|
||||
"confirm": "Confirm",
|
||||
"confirmation-request": "Confirmation request",
|
||||
|
@ -479,7 +526,8 @@
|
|||
"ethereum-node-started-incorrectly-title": "Ethereum node started incorrectly",
|
||||
"etherscan-lookup": "Look up on Etherscan",
|
||||
"export-account": "Export account",
|
||||
"export-key": "Export key",
|
||||
"export-key": "Export private key",
|
||||
"community-private-key": "Community private key",
|
||||
"failed": "Failed",
|
||||
"faq": "Frequently asked questions",
|
||||
"fetch-messages": "↓ Fetch messages",
|
||||
|
@ -490,6 +538,7 @@
|
|||
"fleet": "Fleet",
|
||||
"fleet-settings": "Fleet settings",
|
||||
"follow-your-interests": "Jump into a public chat and meet new people",
|
||||
"follow": "Follow",
|
||||
"free": "↓ Free",
|
||||
"from": "From",
|
||||
"gas-limit": "Gas limit",
|
||||
|
@ -1177,6 +1226,7 @@
|
|||
"validation-amount-invalid-number": "Amount is not a valid number",
|
||||
"validation-amount-is-too-precise": "Amount is too precise. Max number of decimals is {{decimals}}.",
|
||||
"version": "App version",
|
||||
"view": "View",
|
||||
"view-cryptokitties": "View in CryptoKitties",
|
||||
"view-cryptostrikers": "View in CryptoStrikers",
|
||||
"view-etheremon": "View in Etheremon",
|
||||
|
@ -1215,6 +1265,8 @@
|
|||
"welcome-to-status": "Welcome to Status!",
|
||||
"welcome-to-status-description": "Set up your crypto wallet, invite friends to chat and browse decentralized apps",
|
||||
"welcome-blank-message": "Your chats will appear here. To start new chats press the ⊕ button",
|
||||
"welcome-community-blank-message": "Your chats will appear here. To start new chats click on the 3 dots above and select \"Create a channel\"",
|
||||
"welcome-blank-community-message": "Your communities will appear here.",
|
||||
"seed-phrase-placeholder": "Seed phrase...",
|
||||
"word-count": "Word count",
|
||||
"word-n": "Word #{{number}}",
|
||||
|
|