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