parent
1321a85659
commit
34f62d3865
|
@ -82,10 +82,12 @@
|
||||||
(when needs-update?
|
(when needs-update?
|
||||||
(dispatch [:account-update]))))))
|
(dispatch [:account-update]))))))
|
||||||
|
|
||||||
(defn initialize-account [db address]
|
(defn initialize-account [{:keys [accounts] :as db} address]
|
||||||
(let [is-login-screen? (= (:view-id db) :login)]
|
(let [is-login-screen? (= (:view-id db) :login)]
|
||||||
(dispatch [:set :login {}])
|
(dispatch [:set :login {}])
|
||||||
(dispatch [:set :current-account-id address])
|
(dispatch [:set :current-account-id address])
|
||||||
|
(let [key (:public-key (accounts address))]
|
||||||
|
(dispatch [:set :current-public-key key]))
|
||||||
(dispatch [:initialize-account address])
|
(dispatch [:initialize-account address])
|
||||||
(when is-login-screen? (dispatch [:navigate-to-clean default-view]))))
|
(when is-login-screen? (dispatch [:navigate-to-clean default-view]))))
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
{:flex 1})))
|
{:flex 1})))
|
||||||
|
|
||||||
(def account-list
|
(def account-list
|
||||||
{:margin-top 56
|
{:margin-top 20
|
||||||
:height 100})
|
:height 100})
|
||||||
|
|
||||||
(def row-separator
|
(def row-separator
|
||||||
|
|
|
@ -234,11 +234,6 @@
|
||||||
(after #(dispatch [:load-unviewed-messages!]))
|
(after #(dispatch [:load-unviewed-messages!]))
|
||||||
((enrich initialize-chats) load-chats!))
|
((enrich initialize-chats) load-chats!))
|
||||||
|
|
||||||
(register-handler :group-received-msg
|
|
||||||
(u/side-effect!
|
|
||||||
(fn [_ [_ {chat-id :group-id :as msg}]]
|
|
||||||
(messages/save-message chat-id msg))))
|
|
||||||
|
|
||||||
(defmethod nav/preload-data! :chat
|
(defmethod nav/preload-data! :chat
|
||||||
[{:keys [current-chat-id] :as db} [_ _ id]]
|
[{:keys [current-chat-id] :as db} [_ _ id]]
|
||||||
(let [chat-id (or id current-chat-id)
|
(let [chat-id (or id current-chat-id)
|
||||||
|
|
|
@ -16,26 +16,37 @@
|
||||||
:rendered-preview rendered-preview))
|
:rendered-preview rendered-preview))
|
||||||
message))
|
message))
|
||||||
|
|
||||||
(defn store-message [{chat-id :from :as message}]
|
(defn store-message [{chat-id :chat-id :as message}]
|
||||||
(messages/save-message chat-id (dissoc message :rendered-preview :new?)))
|
(messages/save-message chat-id (dissoc message :rendered-preview :new?)))
|
||||||
|
|
||||||
|
(defn get-current-identity
|
||||||
|
[{:keys [current-account-id accounts]}]
|
||||||
|
(:public-key (accounts current-account-id)))
|
||||||
|
|
||||||
|
(defn receive-message
|
||||||
|
[db [_ {:keys [from group-id chat-id msg-id] :as message}]]
|
||||||
|
(let [same-message (messages/get-message msg-id)
|
||||||
|
current-identity (get-current-identity db)]
|
||||||
|
(when-not (or same-message (= from current-identity))
|
||||||
|
(let [group-chat? (not (nil? group-id))
|
||||||
|
chat-id' (or chat-id from)
|
||||||
|
previous-message (messages/get-last-message chat-id')
|
||||||
|
message' (assoc (->> message
|
||||||
|
(cu/check-author-direction previous-message)
|
||||||
|
(check-previev))
|
||||||
|
:delivery-status :pending
|
||||||
|
:chat-id chat-id')]
|
||||||
|
(store-message message')
|
||||||
|
(when-not (c/chat-exists? chat-id')
|
||||||
|
(dispatch [:add-chat chat-id' (when group-chat? {:group-chat true})]))
|
||||||
|
(dispatch [::add-message message'])
|
||||||
|
(when (= (:content-type message') content-type-command-request)
|
||||||
|
(dispatch [:add-request chat-id' message']))
|
||||||
|
(dispatch [:add-unviewed-message chat-id' msg-id])))))
|
||||||
|
|
||||||
(register-handler :received-message
|
(register-handler :received-message
|
||||||
(u/side-effect!
|
(u/side-effect! receive-message))
|
||||||
(fn [{:keys [chats] :as db} [_ {chat-id :from :keys [msg-id] :as message}]]
|
|
||||||
(let [same-message (messages/get-message msg-id)]
|
|
||||||
(when-not same-message
|
|
||||||
(let [message' (assoc (->> message
|
|
||||||
(cu/check-author-direction db chat-id)
|
|
||||||
(check-previev))
|
|
||||||
:delivery-status :pending)]
|
|
||||||
(store-message message')
|
|
||||||
(when-not (c/chat-exists? chat-id)
|
|
||||||
(dispatch [:add-chat chat-id]))
|
|
||||||
(dispatch [::add-message message'])
|
|
||||||
(when (= (:content-type message') content-type-command-request)
|
|
||||||
(dispatch [:add-request chat-id message']))
|
|
||||||
(dispatch [:add-unviewed-message chat-id msg-id])))))))
|
|
||||||
|
|
||||||
(register-handler ::add-message
|
(register-handler ::add-message
|
||||||
(fn [db [_ {chat-id :from :keys [new?] :as message}]]
|
(fn [db [_ {:keys [chat-id new?] :as message}]]
|
||||||
(cu/add-message-to-db db chat-id message new?)))
|
(cu/add-message-to-db db chat-id message new?)))
|
||||||
|
|
|
@ -20,25 +20,6 @@
|
||||||
:seen
|
:seen
|
||||||
:pending))
|
:pending))
|
||||||
|
|
||||||
(defn prepare-message
|
|
||||||
[{:keys [identity current-chat-id] :as db} _]
|
|
||||||
(let [text (get-in db [:chats current-chat-id :input-text])
|
|
||||||
[command] (suggestions/check-suggestion db (str text " "))
|
|
||||||
message (cu/check-author-direction
|
|
||||||
db current-chat-id
|
|
||||||
{:msg-id (random/id)
|
|
||||||
:chat-id current-chat-id
|
|
||||||
:content text
|
|
||||||
:to current-chat-id
|
|
||||||
:from identity
|
|
||||||
:content-type text-content-type
|
|
||||||
:delivery-status (default-delivery-status current-chat-id)
|
|
||||||
:outgoing true
|
|
||||||
:timestamp (time/now-ms)})]
|
|
||||||
(if command
|
|
||||||
(commands/set-command-input db :commands command)
|
|
||||||
(assoc db :new-message (when-not (s/blank? text) message)))))
|
|
||||||
|
|
||||||
(defn prepare-command
|
(defn prepare-command
|
||||||
[identity chat-id {:keys [preview preview-string content command to-message]}]
|
[identity chat-id {:keys [preview preview-string content command to-message]}]
|
||||||
(let [content {:command (command :name)
|
(let [content {:command (command :name)
|
||||||
|
@ -166,14 +147,15 @@
|
||||||
:timestamp (time/now-ms)})
|
:timestamp (time/now-ms)})
|
||||||
params' (assoc params :message message')]
|
params' (assoc params :message message')]
|
||||||
(dispatch [::add-message params'])
|
(dispatch [::add-message params'])
|
||||||
(dispatch [::save-message! params'])
|
(dispatch [::save-message! params'])))))
|
||||||
(dispatch [::send-message! params'])))))
|
|
||||||
|
|
||||||
(register-handler ::add-message
|
(register-handler ::add-message
|
||||||
(fn [db [_ {:keys [chat-id message]}]]
|
(fn [db [_ {:keys [chat-id message]}]]
|
||||||
(cu/add-message-to-db db chat-id message)))
|
(cu/add-message-to-db db chat-id message)))
|
||||||
|
|
||||||
(register-handler ::save-message!
|
(register-handler ::save-message!
|
||||||
|
(after (fn [_ [_ params]]
|
||||||
|
(dispatch [::send-message! params])))
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [_ [_ {:keys [chat-id message]}]]
|
(fn [_ [_ {:keys [chat-id message]}]]
|
||||||
(messages/save-message chat-id message))))
|
(messages/save-message chat-id message))))
|
||||||
|
|
|
@ -207,7 +207,7 @@
|
||||||
(subscribe [:chat-properties [:group-chat :name :contacts :chat-id]])
|
(subscribe [:chat-properties [:group-chat :name :contacts :chat-id]])
|
||||||
show-actions (subscribe [:show-actions])
|
show-actions (subscribe [:show-actions])
|
||||||
contact (subscribe [:get-in [:contacts @chat-id]])]
|
contact (subscribe [:get-in [:contacts @chat-id]])]
|
||||||
(fn []
|
(fn [platform-specific]
|
||||||
[view (st/chat-name-view @show-actions)
|
[view (st/chat-name-view @show-actions)
|
||||||
[text {:style st/chat-name-text
|
[text {:style st/chat-name-text
|
||||||
:platform-specific platform-specific
|
:platform-specific platform-specific
|
||||||
|
@ -241,15 +241,14 @@
|
||||||
[view st/action
|
[view st/action
|
||||||
[chat-icon]]]))))
|
[chat-icon]]]))))
|
||||||
|
|
||||||
(defn chat-toolbar [platform-specific]
|
(defview chat-toolbar [platform-specific]
|
||||||
(let [{:keys [group-chat name contacts]} (subscribe [:chat-properties [:group-chat :name :contacts]])
|
[show-actions [:show-actions]]
|
||||||
show-actions (subscribe [:show-actions])]
|
[view
|
||||||
[view
|
[status-bar {:platform-specific platform-specific}]
|
||||||
[status-bar {:platform-specific platform-specific}]
|
[toolbar {:hide-nav? show-actions
|
||||||
[toolbar {:hide-nav? @show-actions
|
:custom-content [toolbar-content platform-specific]
|
||||||
:custom-content [toolbar-content platform-specific]
|
:custom-action [toolbar-action]
|
||||||
:custom-action [toolbar-action]
|
:style (get-in platform-specific [:styles :components :toolbar])}]])
|
||||||
:style (get-in platform-specific [:styles :components :toolbar])}]]))
|
|
||||||
|
|
||||||
(defview messages-view [platform-specific group-chat]
|
(defview messages-view [platform-specific group-chat]
|
||||||
[messages [:chat :messages]
|
[messages [:chat :messages]
|
||||||
|
@ -305,7 +304,8 @@
|
||||||
[chat-toolbar platform-specific]
|
[chat-toolbar platform-specific]
|
||||||
[messages-container
|
[messages-container
|
||||||
[messages-view platform-specific @group-chat]]
|
[messages-view platform-specific @group-chat]]
|
||||||
(when @group-chat [typing-all platform-specific])
|
;; todo uncomment this
|
||||||
|
#_(when @group-chat [typing-all platform-specific])
|
||||||
[response-view]
|
[response-view]
|
||||||
(when-not @command? [suggestion-container])
|
(when-not @command? [suggestion-container])
|
||||||
[chat-message-new platform-specific]
|
[chat-message-new platform-specific]
|
||||||
|
|
|
@ -52,8 +52,9 @@
|
||||||
(dispatch [:sign-up-confirm (second matches)])))
|
(dispatch [:sign-up-confirm (second matches)])))
|
||||||
|
|
||||||
(defn start-listening-confirmation-code-sms [db]
|
(defn start-listening-confirmation-code-sms [db]
|
||||||
(when (not (:confirmation-code-sms-listener db))
|
(if (not (:confirmation-code-sms-listener db))
|
||||||
(assoc db :confirmation-code-sms-listener (add-sms-listener handle-sms))))
|
(assoc db :confirmation-code-sms-listener (add-sms-listener handle-sms))
|
||||||
|
db))
|
||||||
|
|
||||||
(defn stop-listening-confirmation-code-sms [db]
|
(defn stop-listening-confirmation-code-sms [db]
|
||||||
(when-let [listener (:confirmation-code-sms-listener db)]
|
(when-let [listener (:confirmation-code-sms-listener db)]
|
||||||
|
|
|
@ -238,3 +238,8 @@
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(let [chat-id (subscribe [:get-current-chat-id])]
|
(let [chat-id (subscribe [:get-current-chat-id])]
|
||||||
(reaction (get-in @db [:chats @chat-id :all-loaded?])))))
|
(reaction (get-in @db [:chats @chat-id :all-loaded?])))))
|
||||||
|
|
||||||
|
(register-sub :photo-path
|
||||||
|
(fn [_ [_ id]]
|
||||||
|
(let [contacts (subscribe [:get :contacts])]
|
||||||
|
(reaction (:photo-path (@contacts id))))))
|
||||||
|
|
|
@ -15,13 +15,18 @@
|
||||||
true
|
true
|
||||||
new?))))))
|
new?))))))
|
||||||
|
|
||||||
|
(defn- check-message [previous-message {:keys [from outgoing] :as message}]
|
||||||
|
(merge message
|
||||||
|
{:same-author (if previous-message
|
||||||
|
(= (:from previous-message) from)
|
||||||
|
false)
|
||||||
|
:same-direction (if previous-message
|
||||||
|
(= (:outgoing previous-message) outgoing)
|
||||||
|
true)}))
|
||||||
|
|
||||||
(defn check-author-direction
|
(defn check-author-direction
|
||||||
[db chat-id {:keys [from outgoing] :as message}]
|
([previous-message message]
|
||||||
(let [previous-message (first (get-in db [:chats chat-id :messages]))]
|
(check-message previous-message message))
|
||||||
(merge message
|
([db chat-id message]
|
||||||
{:same-author (if previous-message
|
(let [previous-message (first (get-in db [:chats chat-id :messages]))]
|
||||||
(= (:from previous-message) from)
|
(check-message previous-message message))))
|
||||||
true)
|
|
||||||
:same-direction (if previous-message
|
|
||||||
(= (:outgoing previous-message) outgoing)
|
|
||||||
true)})))
|
|
||||||
|
|
|
@ -159,7 +159,8 @@
|
||||||
:failed "Failed"
|
:failed "Failed"
|
||||||
"Pending")]])
|
"Pending")]])
|
||||||
|
|
||||||
(defn member-photo [{:keys [photo-path]}]
|
(defview member-photo [from]
|
||||||
|
[photo-path [:photo-path from]]
|
||||||
[view st/photo-view
|
[view st/photo-view
|
||||||
[image {:source (if (s/blank? photo-path)
|
[image {:source (if (s/blank? photo-path)
|
||||||
res/user-no-photo
|
res/user-no-photo
|
||||||
|
@ -167,7 +168,7 @@
|
||||||
:style st/photo}]])
|
:style st/photo}]])
|
||||||
|
|
||||||
(defn incoming-group-message-body
|
(defn incoming-group-message-body
|
||||||
[{:keys [selected same-author] :as message} content platform-specific]
|
[{:keys [selected same-author from] :as message} content platform-specific]
|
||||||
(let [delivery-status :seen-by-everyone]
|
(let [delivery-status :seen-by-everyone]
|
||||||
[view st/group-message-wrapper
|
[view st/group-message-wrapper
|
||||||
(when selected
|
(when selected
|
||||||
|
@ -177,7 +178,7 @@
|
||||||
"Mar 7th, 15:22"])
|
"Mar 7th, 15:22"])
|
||||||
[view (st/incoming-group-message-body-st message)
|
[view (st/incoming-group-message-body-st message)
|
||||||
[view st/message-author
|
[view st/message-author
|
||||||
(when (not same-author) [member-photo {}])]
|
(when (not same-author) [member-photo from])]
|
||||||
[view st/group-message-view
|
[view st/group-message-view
|
||||||
content
|
content
|
||||||
;; TODO show for last or selected
|
;; TODO show for last or selected
|
||||||
|
|
|
@ -168,7 +168,7 @@
|
||||||
[view [icon :ok-purple st/add-members-icon]]]
|
[view [icon :ok-purple st/add-members-icon]]]
|
||||||
[touchable-highlight {:style (st/chat-name-btn-edit-container true)
|
[touchable-highlight {:style (st/chat-name-btn-edit-container true)
|
||||||
:on-press focus}
|
:on-press focus}
|
||||||
[text {:style st/chat-name-btn-edit-text} (label :t/edit)]])]
|
[view [text {:style st/chat-name-btn-edit-text} (label :t/edit)]]])]
|
||||||
(when (pos? (count validation-messages))
|
(when (pos? (count validation-messages))
|
||||||
[text {:style st/chat-name-validation-message} (first validation-messages)])])
|
[text {:style st/chat-name-validation-message} (first validation-messages)])])
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,7 @@
|
||||||
:opacity (if enabled? 1 0.3)})
|
:opacity (if enabled? 1 0.3)})
|
||||||
|
|
||||||
(def chat-name-btn-edit-text
|
(def chat-name-btn-edit-text
|
||||||
{:marginTop -1
|
{:color text2-color
|
||||||
:color text2-color
|
|
||||||
:fontFamily font
|
:fontFamily font
|
||||||
:fontSize 16
|
:fontSize 16
|
||||||
:lineHeight 20})
|
:lineHeight 20})
|
||||||
|
@ -118,7 +117,7 @@
|
||||||
:lineHeight 20})
|
:lineHeight 20})
|
||||||
|
|
||||||
(def add-members-icon
|
(def add-members-icon
|
||||||
{:marginVertical 19
|
{:marginVertical -1
|
||||||
:marginLeft 19
|
:marginLeft 19
|
||||||
:marginHorizontal 3
|
:marginHorizontal 3
|
||||||
:width 17
|
:width 17
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
(register-handler :initialize-db
|
(register-handler :initialize-db
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
(realm/reset-account)
|
(realm/reset-account)
|
||||||
(assoc app-db :current-account-id nil)))
|
(assoc app-db :current-account-id nil
|
||||||
|
:current-public-key nil)))
|
||||||
|
|
||||||
(register-handler :initialize-account-db
|
(register-handler :initialize-account-db
|
||||||
(fn [db _]
|
(fn [db _]
|
||||||
|
|
|
@ -76,3 +76,8 @@
|
||||||
(defn get-message [id]
|
(defn get-message [id]
|
||||||
(r/get-one-by-field :account :message :msg-id id))
|
(r/get-one-by-field :account :message :msg-id id))
|
||||||
|
|
||||||
|
(defn get-last-message [chat-id]
|
||||||
|
(-> (r/get-by-field :account :message :chat-id chat-id)
|
||||||
|
(r/sorted :timestamp :desc)
|
||||||
|
(r/single)
|
||||||
|
(js->clj :keywordize-keys true)))
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
[status-im.utils.handlers :refer [register-handler]]
|
[status-im.utils.handlers :refer [register-handler]]
|
||||||
[status-im.components.styles :refer [default-chat-color]]
|
[status-im.components.styles :refer [default-chat-color]]
|
||||||
[status-im.models.chats :as chats]
|
[status-im.models.chats :as chats]
|
||||||
[clojure.string :as s]))
|
[clojure.string :as s]
|
||||||
|
[status-im.utils.handlers :as u]))
|
||||||
|
|
||||||
(defn deselect-contact
|
(defn deselect-contact
|
||||||
[db [_ id]]
|
[db [_ id]]
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
|
|
||||||
(defn prepare-chat
|
(defn prepare-chat
|
||||||
[{:keys [selected-contacts new-group-id] :as db} [_ group-name]]
|
[{:keys [selected-contacts new-group-id] :as db} [_ group-name]]
|
||||||
(let [contacts (mapv #(hash-map :identity %) selected-contacts)
|
(let [contacts (mapv #(hash-map :identity %) selected-contacts)
|
||||||
chat-name (if-not (s/blank? group-name)
|
chat-name (if-not (s/blank? group-name)
|
||||||
group-name
|
group-name
|
||||||
(group-name-from-contacts db))]
|
(group-name-from-contacts db))]
|
||||||
|
@ -87,7 +88,14 @@
|
||||||
|
|
||||||
; todo rewrite
|
; todo rewrite
|
||||||
(register-handler :group-chat-invite-received
|
(register-handler :group-chat-invite-received
|
||||||
(fn [db [action from group-id identities group-name]]
|
(u/side-effect!
|
||||||
(if (chats/chat-exists? group-id)
|
(fn [{:keys [current-public-key] :as db}
|
||||||
(chats/re-join-group-chat db group-id identities group-name)
|
[action from group-id identities group-name]]
|
||||||
(chats/create-chat db group-id identities true group-name))))
|
(if (chats/chat-exists? group-id)
|
||||||
|
(chats/re-join-group-chat db group-id identities group-name)
|
||||||
|
(let [contacts (keep (fn [ident]
|
||||||
|
(when (not= ident current-public-key)
|
||||||
|
{:identity ident})) identities)]
|
||||||
|
(dispatch [:add-chat group-id {:name group-name
|
||||||
|
:group-chat true
|
||||||
|
:contacts contacts}]))))))
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
(fn [_ [action from group-id ack-msg-id]]
|
(fn [_ [action from group-id ack-msg-id]]
|
||||||
(log/debug action from group-id ack-msg-id)
|
(log/debug action from group-id ack-msg-id)
|
||||||
(joined-chat-msg group-id from ack-msg-id))))
|
#_(joined-chat-msg group-id from ack-msg-id))))
|
||||||
|
|
||||||
(register-handler :participant-removed-from-group
|
(register-handler :participant-removed-from-group
|
||||||
(u/side-effect!
|
(u/side-effect!
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
:initialized (let [{:keys [identity]} event]
|
:initialized (let [{:keys [identity]} event]
|
||||||
(dispatch [:protocol-initialized identity]))
|
(dispatch [:protocol-initialized identity]))
|
||||||
:new-msg (let [{:keys [from to payload]} event]
|
:new-msg (let [{:keys [from to payload]} event]
|
||||||
(dispatch [:received-message (assoc payload :from from :to to)]))
|
(dispatch [:received-message (assoc payload
|
||||||
|
:chat-id from
|
||||||
|
:from from
|
||||||
|
:to to)]))
|
||||||
:msg-acked (let [{:keys [msg-id from]} event]
|
:msg-acked (let [{:keys [msg-id from]} event]
|
||||||
(dispatch [:acked-msg from msg-id]))
|
(dispatch [:acked-msg from msg-id]))
|
||||||
:msg-seen (let [{:keys [msg-id from]} event]
|
:msg-seen (let [{:keys [msg-id from]} event]
|
||||||
(dispatch [:msg-seen from msg-id]))
|
(dispatch [:msg-seen from msg-id]))
|
||||||
:delivery-failed (let [{:keys [msg-id from]} event]
|
:delivery-failed (let [{:keys [msg-id from]} event]
|
||||||
(dispatch [:msg-delivery-failed from msg-id]))
|
(dispatch [:msg-delivery-failed from msg-id]))
|
||||||
:new-group-chat (let [{:keys [from group-id identities group-name]} event]
|
:new-group-chat (let [{:keys [from group-id identities group-name]} event]
|
||||||
|
@ -30,8 +33,9 @@
|
||||||
:new-group-msg (let [{from :from
|
:new-group-msg (let [{from :from
|
||||||
group-id :group-id
|
group-id :group-id
|
||||||
payload :payload} event]
|
payload :payload} event]
|
||||||
(dispatch [:group-received-msg (assoc payload :from from
|
(dispatch [:received-message (assoc payload
|
||||||
:group-id group-id)]))
|
:chat-id group-id
|
||||||
|
:from from)]))
|
||||||
:group-chat-invite-acked (let [{:keys [from group-id ack-msg-id]} event]
|
:group-chat-invite-acked (let [{:keys [from group-id ack-msg-id]} event]
|
||||||
(dispatch [:group-chat-invite-acked from group-id ack-msg-id]))
|
(dispatch [:group-chat-invite-acked from group-id ack-msg-id]))
|
||||||
:group-new-participant (let [{:keys [group-id identity from msg-id]} event]
|
:group-new-participant (let [{:keys [group-id identity from msg-id]} event]
|
||||||
|
|
Loading…
Reference in New Issue