The concept of command actions; /browse command actions (back, forward, fullscreen) (#1093)

This commit is contained in:
alwx 2017-04-29 14:08:49 +03:00 committed by Roman Volosovskyi
parent 0359c2819b
commit ff59f8a375
38 changed files with 308 additions and 71 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

View File

@ -1,4 +1,4 @@
function browseSuggestions(params, context) {
function browse(params, context) {
var url;
if (params.url && params.url !== "undefined" && params.url != "") {
@ -11,6 +11,18 @@ function browseSuggestions(params, context) {
return {
title: "Browser",
dynamicTitle: true,
singleLineInput: true,
actions: [
{
type: status.actions.WEB_VIEW_BACK
},
{
type: status.actions.WEB_VIEW_FORWARD
},
{
type: status.actions.FULLSCREEN
},
],
markup: status.components.bridgedWebView(url)
};
}
@ -22,11 +34,10 @@ status.command({
description: I18n.t('browse_description'),
color: "#ffa500",
fullscreen: true,
suggestionsTrigger: 'on-send',
params: [{
name: "url",
type: status.types.TEXT,
placeholder: "URL"
}],
onSend: browseSuggestions
onSend: browse
});

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_action_back.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icon_action_back@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icon_action_back@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_action_forward.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icon_action_forward@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icon_action_forward@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_action_fullscreen_collapse.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icon_action_fullscreen_collapse@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icon_action_fullscreen_collapse@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 B

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_action_fullscreen_expand.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icon_action_fullscreen_expand@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icon_action_fullscreen_expand@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

View File

@ -36,6 +36,7 @@ Command.prototype.create = function (com) {
this["short-preview"] = com.shortPreview;
this["on-send"] = com.onSend;
this.fullscreen = com.fullscreen;
this.actions = com.actions;
this.request = com.request;
this["execute-immediately?"] = com.executeImmediately;
this["sequential-params"] = com.sequentialParams;
@ -189,6 +190,12 @@ var status = {
SET_VALUE: 'set-value',
SET_COMMAND_ARGUMENT: 'set-command-argument'
},
actions: {
WEB_VIEW_BACK: 'web-view-back',
WEB_VIEW_FORWARD: 'web-view-forward',
FULLSCREEN: 'fullscreen',
CUSTOM: 'custom',
},
components: {
view: view,
text: text,

View File

@ -19,7 +19,10 @@
WebViewBridge.onMessage = function (messageString) {
console.log("received from react-native: " + messageString);
var message = JSON.parse(messageString);
if (statusAPI.callbacks[message.event]) {
if (message.event === "actions-execute-js") {
eval(message.js);
} else if (statusAPI.callbacks[message.event]) {
statusAPI.callbacks[message.event](message.params);
}
};

View File

@ -7,10 +7,11 @@
:elevation 2
:translucent? true
:color styles/color-white}
:main {:height 25
:bar-style "dark-content"
:translucent? true
:color styles/color-white}
:main {:height 25
:bar-style "dark-content"
:translucent? true
:color styles/color-white
:expandable-offset 3}
:transparent {:height 25
:bar-style "light-content"
:translucent? true

View File

@ -0,0 +1,27 @@
(ns status-im.chat.styles.input.input-actions
(:require-macros [status-im.utils.styles :refer [defnstyle]])
(:require [status-im.components.styles :as common]))
(def actions-container
{:flex-direction :row
:margin-left 10})
(defn action-view [enabled?]
{:width 38
:height 38
:opacity (if enabled? 1 0.5)
:justify-content :center
:align-items :center})
(def action-view-icon
{:width 24
:height 24})
(def action-view-icon-tinted
{:width 24
:height 24
:tint-color "black"})
(def action-view-fullscreen-expand-icon
{:width 16
:height 16})

View File

@ -10,7 +10,8 @@
[status-im.chat.views.input.animations.responder :as resp]
[status-im.chat.views.input.utils :as input-utils]
[status-im.chat.styles.animations :as style]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[status-im.utils.platform :as p]))
(defn header [key container-height custom-header]
(let [set-container-height (subscribe [:chat-animations key :height])
@ -55,6 +56,7 @@
(let [anim-value (anim/create-value 0)
input-height (subscribe [:chat-ui-props :input-height])
max-height (subscribe [:get-max-container-area-height])
fullscreen? (subscribe [:chat-ui-props :fullscreen?])
chat-input-margin (subscribe [:chat-input-margin])
to-changed-height (subscribe [:chat-animations key :height])
changes-counter (subscribe [:chat-animations key :changes-counter])
@ -69,17 +71,26 @@
:component-did-update
on-update
:component-will-unmount
(if height
#(dispatch [:set-expandable-height key height])
#(dispatch [:choose-predefined-expandable-height key :default]))
(fn []
(dispatch [:set-chat-ui-props {:fullscreen? false}])
(if height
(dispatch [:set-expandable-height key height])
(dispatch [:choose-predefined-expandable-height key :default])))
:reagent-render
(fn [{:keys [draggable? custom-header]} & elements]
@to-changed-height @changes-counter @max-height
(let [bottom (+ @input-height @chat-input-margin)]
(let [{expandable-offset :expandable-offset
status-bar-height :height} (get-in p/platform-specific [:component-styles :status-bar :main])
bottom (+ @input-height @chat-input-margin)
height (if @fullscreen?
(+ @max-height status-bar-height expandable-offset)
anim-value)]
[view style/overlap-container
(when-not hide-overlay?
(when (and (not hide-overlay?)
(not @fullscreen?))
[overlay-view])
(into [animated-view {:style (style/expandable-container anim-value bottom)}
(when draggable?
(into [animated-view {:style (style/expandable-container height bottom)}
(when (and draggable?
(not @fullscreen?))
[header key anim-value custom-header])]
elements)]))})))

View File

@ -5,8 +5,18 @@
[re-frame.core :refer [subscribe dispatch]]
[taoensso.timbre :as log]
[status-im.accessibility-ids :as id]
[status-im.chat.constants :as const]
[status-im.chat.models.input :as input-model]
[status-im.chat.styles.input.input :as style]
[status-im.chat.utils :as chat-utils]
[status-im.chat.views.input.emoji :as emoji]
[status-im.chat.views.input.parameter-box :as parameter-box]
[status-im.chat.views.input.input-actions :as input-actions]
[status-im.chat.views.input.result-box :as result-box]
[status-im.chat.views.input.suggestions :as suggestions]
[status-im.chat.views.input.validation-messages :as validation-messages]
[status-im.components.animation :as anim]
[status-im.components.react :refer [view
autogrow-text-input
animated-view
text
scroll-view
@ -14,19 +24,8 @@
icon
touchable-highlight
dismiss-keyboard!]]
[status-im.chat.models.input :as input-model]
[status-im.chat.views.input.emoji :as emoji]
[status-im.chat.views.input.parameter-box :as parameter-box]
[status-im.chat.views.input.result-box :as result-box]
[status-im.chat.views.input.suggestions :as suggestions]
[status-im.chat.views.input.validation-messages :as validation-messages]
[status-im.chat.styles.input.input :as style]
[status-im.chat.utils :as utils]
[status-im.chat.constants :as const]
[status-im.components.animation :as anim]
[status-im.i18n :as i18n]
[status-im.utils.platform :as platform]
[status-im.chat.utils :as chat-utils]))
[status-im.utils.platform :as platform]))
(defn command-view [first? command]
[touchable-highlight {:on-press #(dispatch [:select-chat-input-command command nil])}
@ -66,27 +65,31 @@
sending-in-progress? (subscribe [:chat-ui-props :sending-in-progress?])
input-focused? (subscribe [:chat-ui-props :input-focused?])
input-ref (atom nil)]
(fn [{:keys [set-layout-height set-container-width height]}]
(fn [{:keys [set-layout-height set-container-width height single-line-input?]}]
[text-input
{:ref #(when %
(dispatch [:set-chat-ui-props {:input-ref %}])
(reset! input-ref %))
:accessibility-label id/chat-message-input
:multiline true
:multiline (not single-line-input?)
:default-value (or @input-text "")
:editable true
:blur-on-submit false
:on-submit-editing #(.setNativeProps @input-ref (clj->js {:text (str @input-text "\n")})) ;because of bug on Android, next line is not inserted
:editable (not @sending-in-progress?)
:blur-on-submit single-line-input?
:on-focus #(dispatch [:set-chat-ui-props {:input-focused? true
:show-emoji? false}])
:on-blur #(do (dispatch [:set-chat-ui-props {:input-focused? false}]))
:on-submit-editing (fn [e]
(when single-line-input?
(dispatch [:send-current-message])
(.setNativeProps @input-ref (clj->js {:text (str @input-text "\n")}))))
:on-layout (fn [e]
(set-container-width (.-width (.-layout (.-nativeEvent e)))))
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
height (.. native-event -contentSize -height)
text (.-text native-event)]
(set-layout-height height)
(when-not single-line-input?
(let [height (.. native-event -contentSize -height)]
(set-layout-height height)))
(when (not= text @input-text)
(dispatch [:set-chat-input-text text])
(if @command
@ -97,7 +100,8 @@
(dispatch [:set-chat-input-metadata nil])
(dispatch [:set-chat-ui-props {:result-box nil
:validation-messages nil}]))))))
:on-content-size-change (when-not input-focused?
:on-content-size-change (when (and (not @input-focused?)
(not single-line-input?))
#(let [h (-> (.-nativeEvent %)
(.-contentSize)
(.-height))]
@ -179,14 +183,15 @@
command (subscribe [:selected-chat-command])]
(r/create-class
{:reagent-render
(fn [{:keys [anim-margin]}]
(fn [{:keys [anim-margin single-line-input?]}]
(let [{:keys [width height container-width]} (r/state component)
command @command]
[animated-view {:style (style/input-root height anim-margin)}
[invisible-input {:set-layout-width set-layout-width}]
[basic-text-input {:set-layout-height set-layout-height
:set-container-width set-container-width
:height height}]
:height height
:single-line-input? single-line-input?}]
[input-helper {:command command
:width width}]
[seq-input {:command-width width}]
@ -196,35 +201,41 @@
(dismiss-keyboard!))}
[view
[icon :smile style/input-emoji-icon]]]
[touchable-highlight
{:on-press #(do (dispatch [:set-chat-input-text nil])
(dispatch [:set-chat-input-metadata nil])
(dispatch [:set-chat-ui-props {:result-box nil
:validation-messages nil}])
(dispatch [:clear-seq-arguments]))}
[view style/input-clear-container
[icon :close_gray style/input-clear-icon]]])]))})))
(when-not single-line-input?
[touchable-highlight
{:on-press #(do (dispatch [:set-chat-input-text nil])
(dispatch [:set-chat-input-metadata nil])
(dispatch [:set-chat-ui-props {:result-box nil
:validation-messages nil}])
(dispatch [:clear-seq-arguments]))}
[view style/input-clear-container
[icon :close_gray style/input-clear-icon]]]))]))})))
(defview input-container [{:keys [anim-margin]}]
[command-completion [:command-completion]
selected-command [:selected-chat-command]
input-text [:chat :input-text]
seq-arg-input-text [:chat :seq-argument-input-text]]
[view style/input-container
[input-view {:anim-margin anim-margin}]
(when (and (not (str/blank? input-text))
(or (not selected-command)
(some #{:complete :less-than-needed} [command-completion])))
[touchable-highlight {:on-press #(if (get-in selected-command [:command :sequential-params])
(do
(when-not (str/blank? seq-arg-input-text)
(dispatch [:send-seq-argument]))
(js/setTimeout
(fn [] (dispatch [:chat-input-focus :seq-input-ref]))
100))
(dispatch [:send-current-message]))}
[view style/send-message-container
[icon :arrow_top style/send-message-icon]]])])
seq-arg-input-text [:chat :seq-argument-input-text]
result-box [:chat-ui-props :result-box]]
(let [single-line-input? (:singleLineInput result-box)]
[view style/input-container
[input-view {:anim-margin anim-margin
:single-line-input? single-line-input?}]
(if (:actions result-box)
[input-actions/input-actions-view]
(when (and (not (str/blank? input-text))
(or (not selected-command)
(some #{:complete :less-than-needed} [command-completion])))
[touchable-highlight {:on-press #(if (get-in selected-command [:command :sequential-params])
(do
(when-not (str/blank? seq-arg-input-text)
(dispatch [:send-seq-argument]))
(js/setTimeout
(fn [] (dispatch [:chat-input-focus :seq-input-ref]))
100))
(dispatch [:send-current-message]))}
[view style/send-message-container
[icon :arrow_top style/send-message-icon]]]))]))
(defn container []
(let [margin (subscribe [:chat-input-margin])

View File

@ -0,0 +1,68 @@
(ns status-im.chat.views.input.input-actions
(:require-macros [status-im.utils.views :refer [defview]])
(:require [clojure.string :as str]
[reagent.core :as r]
[re-frame.core :refer [subscribe dispatch]]
[taoensso.timbre :as log]
[status-im.components.react :refer [view
text
icon
touchable-highlight]]
[status-im.chat.styles.input.input-actions :as style]))
(defmulti action-view (fn [{:keys [type]}] (keyword type)))
(defmethod action-view :fullscreen
[_]
(let [fullscreen? (subscribe [:chat-ui-props :fullscreen?])]
(fn []
[touchable-highlight
{:on-press #(dispatch [:set-chat-ui-props {:fullscreen? (not @fullscreen?)}])}
(if @fullscreen?
[view (style/action-view true)
[icon :action_fullscreen_collapse style/action-view-icon]]
[view (style/action-view true)
[icon :action_fullscreen_expand style/action-view-fullscreen-expand-icon]])])))
(defmethod action-view :web-view-back
[_]
(let [result-box (subscribe [:chat-ui-props :result-box])
webview (subscribe [:get :webview-bridge])]
(fn []
(if (:can-go-back? @result-box)
[touchable-highlight
{:on-press #(.goBack @webview)}
[view (style/action-view true)
[icon :action_back style/action-view-icon-tinted]]]
[view (style/action-view false)
[icon :action_back style/action-view-icon]]))))
(defmethod action-view :web-view-forward
[_]
(let [result-box (subscribe [:chat-ui-props :result-box])
webview (subscribe [:get :webview-bridge])]
(fn []
(if (:can-go-forward? @result-box)
[touchable-highlight
{:on-press #(.goForward @webview)}
[view (style/action-view true)
[icon :action_forward style/action-view-icon-tinted]]]
[view (style/action-view false)
[icon :action_forward style/action-view-icon]]))))
(defmethod action-view :default
[{:keys [image executeJs]}]
[touchable-highlight
{:on-press #(dispatch [:send-to-webview-bridge {:event "actions-execute-js"
:js executeJs}])}
[view (style/action-view true)
[icon (str "action_" image) style/action-view-icon]]])
(defn input-actions-view []
(let [result-box (subscribe [:chat-ui-props :result-box])]
(fn []
(let [{:keys [actions]} @result-box]
[view style/actions-container
(for [{:keys [type] :as action} actions]
^{:key type}
[action-view action])]))))

View File

@ -5,18 +5,23 @@
[status-im.components.webview-bridge :refer [webview-bridge]]
[status-im.components.react :refer [view
text]]
[status-im.components.status :as status]
[status-im.i18n :refer [label]]
[status-im.utils.js-resources :as js-res]
[clojure.string :as str]))
[clojure.string :as str]
[taoensso.timbre :as log]))
(defn on-navigation-change
[event {:keys [dynamicTitle] :as result-box}]
(let [{:strs [loading url title]} (js->clj event)]
[event {:keys [dynamicTitle actions] :as result-box}]
(let [{:strs [loading url title canGoBack canGoForward]} (js->clj event)]
(when-not (= "about:blank" url)
(when-not loading
(dispatch [:set-command-argument [0 url]]))
(when (and dynamicTitle (not (str/blank? title)))
(dispatch [:set-chat-ui-props {:result-box (assoc result-box :title title)}])))))
(let [result-box (assoc result-box :can-go-back? canGoBack :can-go-forward? canGoForward)
result-box (if (and dynamicTitle (not (str/blank? title)))
(assoc result-box :title title)
result-box)]
(dispatch [:set-chat-ui-props {:result-box result-box}])))))
(defn web-view-error []
(r/as-element

View File

@ -7,9 +7,10 @@
{:status-bar {:default {:height 20
:bar-style "default"
:color styles/color-white}
:main {:height 20
:bar-style "default"
:color styles/color-white}
:main {:height 20
:bar-style "default"
:color styles/color-white
:expandable-offset 8}
:transparent {:height 20
:bar-style "light-content"
:color styles/color-transparent}