• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "frameworks/bridge/declarative_frontend/jsview/action_sheet/js_action_sheet.h"
17 
18 #include <string>
19 #include <vector>
20 
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/common/utils/engine_helper.h"
23 #include "bridge/declarative_frontend/engine/functions/js_function.h"
24 #include "bridge/declarative_frontend/view_stack_processor.h"
25 #include "core/common/container.h"
26 #include "core/components/dialog/dialog_component.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 
29 namespace OHOS::Ace::Framework {
30 namespace {
31 const DimensionOffset ACTION_SHEET_OFFSET_DEFAULT = DimensionOffset(0.0_vp, -40.0_vp);
32 const std::vector<DialogAlignment> DIALOG_ALIGNMENT = { DialogAlignment::TOP, DialogAlignment::CENTER,
33     DialogAlignment::BOTTOM, DialogAlignment::DEFAULT, DialogAlignment::TOP_START, DialogAlignment::TOP_END,
34     DialogAlignment::CENTER_START, DialogAlignment::CENTER_END, DialogAlignment::BOTTOM_START,
35     DialogAlignment::BOTTOM_END };
36 } // namespace
37 
ParseSheetInfo(const JSCallbackInfo & args,JSRef<JSVal> val)38 ActionSheetInfo ParseSheetInfo(const JSCallbackInfo& args, JSRef<JSVal> val)
39 {
40     ActionSheetInfo sheetInfo;
41     if (!val->IsObject()) {
42         LOGW("param is not an object.");
43         return sheetInfo;
44     }
45 
46     auto obj = JSRef<JSObject>::Cast(val);
47     auto titleVal = obj->GetProperty("title");
48     std::string title;
49     if (JSActionSheet::ParseJsString(titleVal, title)) {
50         sheetInfo.title = title;
51     }
52 
53     auto iconVal = obj->GetProperty("icon");
54     std::string icon;
55     if (JSActionSheet::ParseJsMedia(iconVal, icon)) {
56         sheetInfo.icon = icon;
57     }
58 
59     auto actionValue = obj->GetProperty("action");
60     if (actionValue->IsFunction()) {
61         auto actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
62         // NG
63         if (Container::IsCurrentUseNewPipeline()) {
64             auto callback = [execCtx = args.GetExecutionContext(), func = std::move(actionFunc)](
65                                 const GestureEvent& /*info*/) {
66                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
67                 ACE_SCORING_EVENT("SheetInfo.action");
68                 func->ExecuteJS();
69                 LOGD("action triggered");
70             };
71             sheetInfo.action = AceType::MakeRefPtr<NG::ClickEvent>(callback);
72             return sheetInfo;
73         }
74 
75         RefPtr<Gesture> tapGesture = AceType::MakeRefPtr<TapGesture>();
76         tapGesture->SetOnActionId(
77             [execCtx = args.GetExecutionContext(), func = std::move(actionFunc)](const GestureEvent& info) {
78                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
79                 ACE_SCORING_EVENT("SheetInfo.action");
80                 func->Execute();
81                 // Close dialog when click sheet.
82                 auto container = Container::Current();
83                 if (!container) {
84                     return;
85                 }
86                 auto context = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
87                 if (!context) {
88                     return;
89                 }
90                 auto stack = context->GetLastStack();
91                 if (!stack) {
92                     return;
93                 }
94                 stack->PopDialog();
95             });
96         sheetInfo.gesture = tapGesture;
97     }
98     return sheetInfo;
99 }
100 
Show(const JSCallbackInfo & args)101 void JSActionSheet::Show(const JSCallbackInfo& args)
102 {
103     LOGD("show ActionSheet");
104     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
105     if (!scopedDelegate) {
106         // this case usually means there is no foreground container, need to figure out the reason.
107         LOGE("scopedDelegate is null, please check");
108         return;
109     }
110     if (!args[0]->IsObject()) {
111         LOGE("args is not an object, can't show ActionSheet.");
112         return;
113     }
114 
115     DialogProperties properties {
116         .type = DialogType::ACTION_SHEET, .alignment = DialogAlignment::BOTTOM, .offset = ACTION_SHEET_OFFSET_DEFAULT
117     };
118     auto obj = JSRef<JSObject>::Cast(args[0]);
119     // Parse title.
120     auto titleValue = obj->GetProperty("title");
121     std::string title;
122     if (ParseJsString(titleValue, title)) {
123         properties.title = title;
124     }
125 
126     // Parses message.
127     auto messageValue = obj->GetProperty("message");
128     std::string message;
129     if (ParseJsString(messageValue, message)) {
130         properties.content = message;
131     }
132 
133     // Parse auto autoCancel.
134     auto autoCancelValue = obj->GetProperty("autoCancel");
135     if (autoCancelValue->IsBoolean()) {
136         properties.autoCancel = autoCancelValue->ToBoolean();
137     }
138 
139     // Parse cancel.
140     auto cancelValue = obj->GetProperty("cancel");
141     if (cancelValue->IsFunction()) {
142         auto cancelFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(cancelValue));
143         // NG
144         if (Container::IsCurrentUseNewPipeline()) {
145             properties.onCancel = [execCtx = args.GetExecutionContext(), func = std::move(cancelFunc)]() {
146                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
147                 ACE_SCORING_EVENT("ActionSheet.cancel");
148                 func->ExecuteJS();
149                 LOGD("actionSheet cancel triggered");
150             };
151         } else {
152             EventMarker cancelId([execCtx = args.GetExecutionContext(), func = std::move(cancelFunc)]() {
153                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
154                 ACE_SCORING_EVENT("ActionSheet.cancel");
155                 func->Execute();
156             });
157             properties.callbacks.try_emplace("cancel", cancelId);
158         }
159     }
160 
161     // Parse confirm.
162     auto confirmVal = obj->GetProperty("confirm");
163     if (confirmVal->IsObject()) {
164         JSRef<JSObject> confirmObj = JSRef<JSObject>::Cast(confirmVal);
165         JSRef<JSVal> value = confirmObj->GetProperty("value");
166         std::string buttonValue;
167         if (ParseJsString(value, buttonValue)) {
168             ButtonInfo buttonInfo = { .text = buttonValue, .textColor = "blue" };
169             JSRef<JSVal> actionValue = confirmObj->GetProperty("action");
170             // parse confirm action
171             if (actionValue->IsFunction()) {
172                 auto actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
173                 // NG
174                 if (Container::IsCurrentUseNewPipeline()) {
175                     auto callback = [execCtx = args.GetExecutionContext(), func = std::move(actionFunc)](
176                                         GestureEvent& /*info*/) {
177                         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
178                         ACE_SCORING_EVENT("ActionSheet.confirm.action");
179                         LOGD("actionSheet confirm triggered");
180                         func->ExecuteJS();
181                     };
182                     buttonInfo.action = AceType::MakeRefPtr<NG::ClickEvent>(std::move(callback));
183                 } else {
184                     EventMarker actionId([execCtx = args.GetExecutionContext(), func = std::move(actionFunc)]() {
185                         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
186                         ACE_SCORING_EVENT("ActionSheet.confirm.action");
187                         func->Execute();
188                     });
189                     properties.primaryId = actionId;
190                 }
191             }
192             properties.buttons.emplace_back(buttonInfo);
193         }
194     }
195 
196     // Parse sheets
197     auto sheetsVal = obj->GetProperty("sheets");
198     if (sheetsVal->IsArray()) {
199         std::vector<ActionSheetInfo> sheetsInfo;
200         auto sheetsArr = JSRef<JSArray>::Cast(sheetsVal);
201         for (size_t index = 0; index < sheetsArr->Length(); ++index) {
202             sheetsInfo.emplace_back(ParseSheetInfo(args, sheetsArr->GetValueAt(index)));
203         }
204         properties.sheetsInfo = std::move(sheetsInfo);
205     }
206 
207     // Parse alignment
208     auto alignmentValue = obj->GetProperty("alignment");
209     if (alignmentValue->IsNumber()) {
210         auto alignment = alignmentValue->ToNumber<int32_t>();
211         if (alignment >= 0 && alignment <= static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
212             properties.alignment = DIALOG_ALIGNMENT[alignment];
213         }
214     }
215 
216     // Parse offset
217     auto offsetValue = obj->GetProperty("offset");
218     if (offsetValue->IsObject()) {
219         auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
220         Dimension dx;
221         auto dxValue = offsetObj->GetProperty("dx");
222         ParseJsDimensionVp(dxValue, dx);
223         Dimension dy;
224         auto dyValue = offsetObj->GetProperty("dy");
225         ParseJsDimensionVp(dyValue, dy);
226         properties.offset = DimensionOffset(dx, dy);
227     }
228 
229     // Parses gridCount.
230     auto gridCountValue = obj->GetProperty("gridCount");
231     if (gridCountValue->IsNumber()) {
232         properties.gridCount = gridCountValue->ToNumber<int32_t>();
233     }
234 
235     // NG
236     if (Container::IsCurrentUseNewPipeline()) {
237         auto container = Container::Current();
238         CHECK_NULL_VOID(container);
239         auto pipelineContext = container->GetPipelineContext();
240         CHECK_NULL_VOID(pipelineContext);
241         auto context = AceType::DynamicCast<NG::PipelineContext>(pipelineContext);
242         CHECK_NULL_VOID(context);
243         auto overlayManager = context->GetOverlayManager();
244         CHECK_NULL_VOID(overlayManager);
245         overlayManager->ShowDialog(properties, nullptr, false);
246         args.SetReturnValue(args.This());
247         return;
248     }
249 
250     // Show ActionSheet.
251     auto container = Container::Current();
252     if (container) {
253         auto context = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
254         auto executor = container->GetTaskExecutor();
255         if (executor) {
256             executor->PostTask(
257                 [context, properties]() {
258                     if (context) {
259                         context->ShowDialog(properties, false, "ActionSheet");
260                     }
261                 },
262                 TaskExecutor::TaskType::UI);
263         }
264     }
265     args.SetReturnValue(args.This());
266 }
267 
JSBind(BindingTarget globalObj)268 void JSActionSheet::JSBind(BindingTarget globalObj)
269 {
270     JSClass<JSActionSheet>::Declare("ActionSheet");
271     JSClass<JSActionSheet>::StaticMethod("show", &JSActionSheet::Show);
272     JSClass<JSActionSheet>::Inherit<JSViewAbstract>();
273     JSClass<JSActionSheet>::Bind<>(globalObj);
274 }
275 
276 } // namespace OHOS::Ace::Framework
277