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/js_lazy_foreach.h"
17
18 #include <functional>
19 #include <set>
20 #include <string>
21
22 #include "base/memory/ace_type.h"
23 #include "base/memory/referenced.h"
24 #include "base/utils/utils.h"
25 #include "bridge/common/utils/utils.h"
26 #include "bridge/declarative_frontend/engine/js_object_template.h"
27 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_actuator.h"
28 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_builder.h"
29 #ifndef NG_BUILD
30 #include "bridge/declarative_frontend/jsview/js_lazy_foreach_component.h"
31 #endif
32 #include "bridge/declarative_frontend/jsview/js_view.h"
33 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
34 #include "bridge/declarative_frontend/jsview/models/lazy_for_each_model_impl.h"
35 #include "bridge/declarative_frontend/view_stack_processor.h"
36 #include "core/common/container.h"
37 #include "core/common/container_scope.h"
38 #include "core/components_ng/base/view_stack_model.h"
39 #include "core/components_ng/syntax/lazy_for_each_model.h"
40 #include "core/components_ng/syntax/lazy_for_each_model_ng.h"
41
42 namespace OHOS::Ace {
43
44 std::unique_ptr<LazyForEachModel> LazyForEachModel::instance_ = nullptr;
45 std::mutex LazyForEachModel::mutex_;
46
GetInstance()47 LazyForEachModel* LazyForEachModel::GetInstance()
48 {
49 if (!instance_) {
50 std::lock_guard<std::mutex> lock(mutex_);
51 if (!instance_) {
52 #ifdef NG_BUILD
53 instance_.reset(new NG::LazyForEachModelNG());
54 #else
55 if (Container::IsCurrentUseNewPipeline()) {
56 instance_.reset(new NG::LazyForEachModelNG());
57 } else {
58 instance_.reset(new Framework::LazyForEachModelImpl());
59 }
60 #endif
61 }
62 }
63 return instance_.get();
64 }
65
66 } // namespace OHOS::Ace
67
68 namespace OHOS::Ace::Framework {
69
JSBind(BindingTarget globalObj)70 void JSDataChangeListener::JSBind(BindingTarget globalObj)
71 {
72 JSClass<JSDataChangeListener>::Declare("__ohos_ace_inner_JSDataChangeListener__");
73 // API7 onEditChanged deprecated
74 JSClass<JSDataChangeListener>::CustomMethod("onDataReloaded", &JSDataChangeListener::OnDataReloaded);
75 JSClass<JSDataChangeListener>::CustomMethod("onDataReload", &JSDataChangeListener::OnDataReloaded);
76 // API7 onDataAdded deprecated
77 JSClass<JSDataChangeListener>::CustomMethod("onDataAdded", &JSDataChangeListener::OnDataAdded);
78 JSClass<JSDataChangeListener>::CustomMethod("onDataAdd", &JSDataChangeListener::OnDataAdded);
79 // API7 onDataDeleted deprecated
80 JSClass<JSDataChangeListener>::CustomMethod("onDataDeleted", &JSDataChangeListener::OnDataDeleted);
81 JSClass<JSDataChangeListener>::CustomMethod("onDataDelete", &JSDataChangeListener::OnDataDeleted);
82 // API7 onDataChanged deprecated
83 JSClass<JSDataChangeListener>::CustomMethod("onDataChanged", &JSDataChangeListener::OnDataChanged);
84 JSClass<JSDataChangeListener>::CustomMethod("onDataChange", &JSDataChangeListener::OnDataChanged);
85 // API7 onDataMoved deprecated
86 JSClass<JSDataChangeListener>::CustomMethod("onDataMoved", &JSDataChangeListener::OnDataMoved);
87 JSClass<JSDataChangeListener>::CustomMethod("onDataMove", &JSDataChangeListener::OnDataMoved);
88 JSClass<JSDataChangeListener>::Bind(
89 globalObj, &JSDataChangeListener::Constructor, &JSDataChangeListener::Destructor);
90 }
91
CreateActuator(const std::string viewId)92 RefPtr<JSLazyForEachActuator> CreateActuator(const std::string viewId)
93 {
94 #ifdef NG_BUILD
95 return AceType::MakeRefPtr<JSLazyForEachBuilder>();
96 #else
97 if (Container::IsCurrentUseNewPipeline()) {
98 return AceType::MakeRefPtr<JSLazyForEachBuilder>();
99 } else {
100 return AceType::MakeRefPtr<JSLazyForEachComponent>(viewId);
101 }
102 #endif
103 }
104
105 namespace {
106
107 enum {
108 PARAM_VIEW_ID = 0,
109 PARAM_PARENT_VIEW,
110 PARAM_DATA_SOURCE,
111 PARAM_ITEM_GENERATOR,
112 PARAM_KEY_GENERATOR,
113
114 MIN_PARAM_SIZE = PARAM_KEY_GENERATOR,
115 MAX_PARAM_SIZE,
116 };
117
ParseAndVerifyParams(const JSCallbackInfo & info,JSRef<JSVal> (& params)[MAX_PARAM_SIZE])118 bool ParseAndVerifyParams(const JSCallbackInfo& info, JSRef<JSVal> (¶ms)[MAX_PARAM_SIZE])
119 {
120 if (info.Length() < MIN_PARAM_SIZE) {
121 return false;
122 }
123
124 if (!info[PARAM_VIEW_ID]->IsNumber() && !info[PARAM_VIEW_ID]->IsString()) {
125 return false;
126 }
127 if (!info[PARAM_PARENT_VIEW]->IsObject()) {
128 return false;
129 }
130 if (!info[PARAM_DATA_SOURCE]->IsObject()) {
131 return false;
132 }
133 if (!info[PARAM_ITEM_GENERATOR]->IsFunction()) {
134 return false;
135 }
136 if (info.Length() > MIN_PARAM_SIZE && !info[PARAM_KEY_GENERATOR]->IsFunction()) {
137 return false;
138 }
139
140 for (int32_t idx = PARAM_VIEW_ID; idx < std::min(info.Length(), static_cast<int32_t>(MAX_PARAM_SIZE)); ++idx) {
141 params[idx] = info[idx];
142 }
143 return true;
144 }
145
146 } // namespace
147
JSBind(BindingTarget globalObj)148 void JSLazyForEach::JSBind(BindingTarget globalObj)
149 {
150 JSClass<JSLazyForEach>::Declare("LazyForEach");
151 JSClass<JSLazyForEach>::StaticMethod("create", &JSLazyForEach::Create);
152 JSClass<JSLazyForEach>::StaticMethod("pop", &JSLazyForEach::Pop);
153 JSClass<JSLazyForEach>::Bind(globalObj);
154
155 JSDataChangeListener::JSBind(globalObj);
156 }
157
Create(const JSCallbackInfo & info)158 void JSLazyForEach::Create(const JSCallbackInfo& info)
159 {
160 JSRef<JSVal> params[MAX_PARAM_SIZE];
161 if (!ParseAndVerifyParams(info, params)) {
162 LOGE("Invalid arguments for LazyForEach");
163 return;
164 }
165 std::string viewId = ViewStackModel::GetInstance()->ProcessViewId(params[PARAM_VIEW_ID]->ToString());
166
167 JSRef<JSObject> parentViewObj = JSRef<JSObject>::Cast(params[PARAM_PARENT_VIEW]);
168 JSRef<JSObject> dataSourceObj = JSRef<JSObject>::Cast(params[PARAM_DATA_SOURCE]);
169 JSRef<JSFunc> itemGenerator = JSRef<JSFunc>::Cast(params[PARAM_ITEM_GENERATOR]);
170 ItemKeyGenerator keyGenFunc;
171 if (params[PARAM_KEY_GENERATOR]->IsUndefined()) {
172 keyGenFunc = [viewId](const JSRef<JSVal>&, size_t index) { return viewId + "-" + std::to_string(index); };
173 } else {
174 keyGenFunc = [viewId, keyGenerator = JSRef<JSFunc>::Cast(params[PARAM_KEY_GENERATOR])](
175 const JSRef<JSVal>& jsVal, size_t index) {
176 JSRef<JSVal> params[] = { jsVal, JSRef<JSVal>::Make(ToJSValue(index)) };
177 auto key = keyGenerator->Call(JSRef<JSObject>(), ArraySize(params), params);
178 return viewId + "-" + (key->IsString() || key->IsNumber() ? key->ToString() : std::to_string(index));
179 };
180 }
181
182 const auto& actuator = CreateActuator(viewId);
183 actuator->SetJSExecutionContext(info.GetExecutionContext());
184 actuator->SetParentViewObj(parentViewObj);
185 actuator->SetDataSourceObj(dataSourceObj);
186 actuator->SetItemGenerator(itemGenerator, std::move(keyGenFunc));
187 LazyForEachModel::GetInstance()->Create(actuator);
188 }
189
Pop()190 void JSLazyForEach::Pop()
191 {
192 auto* stack = NG::ViewStackProcessor::GetInstance();
193 if (stack->GetMainFrameNode() && stack->GetMainFrameNode()->GetTag() == V2::TABS_ETS_TAG) {
194 return;
195 }
196 ViewStackModel::GetInstance()->PopContainer();
197 }
198
199 } // namespace OHOS::Ace::Framework
200