Skip to content

Commit 424b979

Browse files
committed
2 parents b3ad2e4 + a258c9f commit 424b979

9 files changed

+98
-12
lines changed

lib/framework/action.dart

+24
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import 'package:ensemble/framework/widget/view_util.dart';
2727
import 'package:ensemble/receive_intent_manager.dart';
2828
import 'package:ensemble/screen_controller.dart';
2929
import 'package:ensemble/util/utils.dart';
30+
import 'package:ensemble/widget/stub_widgets.dart';
3031
import 'package:ensemble_ts_interpreter/invokables/invokable.dart';
3132
import 'package:flutter/material.dart';
3233
import 'package:source_span/source_span.dart';
@@ -980,6 +981,25 @@ class SaveKeychain extends EnsembleAction {
980981
}
981982
}
982983

984+
class SignInAnonymousAction extends EnsembleAction {
985+
final EnsembleAction? onAuthenticated;
986+
final EnsembleAction? onError;
987+
988+
SignInAnonymousAction({
989+
super.initiator,
990+
super.inputs,
991+
required this.onAuthenticated,
992+
required this.onError,
993+
});
994+
995+
factory SignInAnonymousAction.fromYaml({Map? payload}) {
996+
return SignInAnonymousAction(
997+
onAuthenticated: EnsembleAction.fromYaml(payload?['onAuthenticated']),
998+
onError: EnsembleAction.fromYaml(payload?['onError']),
999+
);
1000+
}
1001+
}
1002+
9831003
class ClearKeychain extends EnsembleAction {
9841004
ClearKeychain({
9851005
required this.key,
@@ -1083,6 +1103,8 @@ enum ActionType {
10831103
callNativeMethod,
10841104
deeplinkInit,
10851105
authenticateByBiometric,
1106+
setLanguage,
1107+
signInAnonymous,
10861108
handleDeeplink,
10871109
createDeeplink,
10881110
verifySignIn,
@@ -1193,6 +1215,8 @@ abstract class EnsembleAction {
11931215
return FilePickerAction.fromYaml(payload: payload);
11941216
} else if (actionType == ActionType.uploadFiles) {
11951217
return FileUploadAction.fromYaml(payload: payload);
1218+
} else if (actionType == ActionType.signInAnonymous) {
1219+
return SignInAnonymousAction.fromYaml(payload: payload);
11961220
} else if (actionType == ActionType.pickFiles) {
11971221
return FilePickerAction.fromYaml(payload: payload);
11981222
} else if (actionType == ActionType.openUrl) {

lib/framework/stub/auth_context_manager.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ class AuthenticatedUser with Invokable {
3737
this.emailLVerified,
3838
this.tenantId,
3939
this.creationTime,
40-
this.lastSignInTime});
40+
this.lastSignInTime,
41+
this.isAnonymous = false});
4142

4243
final SignInClient? client;
4344
SignInProvider? provider;
@@ -55,6 +56,8 @@ class AuthenticatedUser with Invokable {
5556
DateTime? creationTime;
5657
DateTime? lastSignInTime;
5758

59+
bool isAnonymous;
60+
5861
// optional server credentials (bearer token, cookies, ....) associated with this user account
5962
// TODO: move to secure storage
6063
Map? data;

lib/framework/theme_manager.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ class EnsembleTheme {
268268
String key = '.$className'; // Prepend '.' to match the style keys
269269
var classStyle = styles[key];
270270
if (classStyle != null) {
271-
resolvedStyles = {...resolvedStyles, ...classStyle};
271+
resolvedStyles = mergeMaps(resolvedStyles, classStyle);
272272
}
273273
}
274274
return resolvedStyles;

lib/layout/helpers/list_view_core.dart

+23-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class ListViewCore extends StatefulWidget {
6969
this.loadingBuilder,
7070
this.errorBuilder,
7171
this.separatorBuilder,
72+
this.onScroll,
7273
});
7374

7475
final bool shrinkWrap;
@@ -89,20 +90,27 @@ class ListViewCore extends StatefulWidget {
8990
final WidgetBuilder? errorBuilder;
9091
final IndexedWidgetBuilder? separatorBuilder;
9192
final ItemBuilder itemBuilder;
93+
final void Function(double)? onScroll;
9294

9395
@override
9496
State<ListViewCore> createState() => _ListViewCoreState();
9597
}
9698

9799
class _ListViewCoreState extends State<ListViewCore> {
98100
late final Debouncer debounce;
101+
late final Debouncer _scrollDebouce;
102+
late final ScrollController _scrollController;
99103

100104
int? _lastFetchedIndex;
101105

102106
@override
103107
void initState() {
104108
super.initState();
105109
debounce = Debouncer(widget.debounceDuration);
110+
_scrollDebouce = Debouncer(const Duration(milliseconds: 15));
111+
_scrollController = widget.scrollController ?? ScrollController();
112+
_scrollController.addListener(_onScroll);
113+
106114
attemptFetch();
107115
}
108116

@@ -116,8 +124,13 @@ class _ListViewCoreState extends State<ListViewCore> {
116124

117125
@override
118126
void dispose() {
119-
super.dispose();
127+
_scrollController.removeListener(_onScroll);
128+
if (widget.scrollController == null) {
129+
_scrollController.dispose();
130+
}
120131
debounce.cancel();
132+
_scrollDebouce.cancel();
133+
super.dispose();
121134
}
122135

123136
void attemptFetch() {
@@ -135,6 +148,14 @@ class _ListViewCoreState extends State<ListViewCore> {
135148
}
136149
}
137150

151+
void _onScroll() {
152+
if (widget.onScroll != null) {
153+
_scrollDebouce.run(() {
154+
widget.onScroll?.call(_scrollController.position.pixels);
155+
});
156+
}
157+
}
158+
138159
WidgetBuilder get loadingBuilder =>
139160
widget.loadingBuilder ??
140161
(context) => const Center(child: flutter.CircularProgressIndicator());
@@ -162,7 +183,7 @@ class _ListViewCoreState extends State<ListViewCore> {
162183
scrollDirection: widget.scrollDirection,
163184
reverse: widget.reverse,
164185
shrinkWrap: widget.shrinkWrap,
165-
controller: widget.scrollController,
186+
controller: _scrollController,
166187
physics: widget.physics,
167188
cacheExtent: widget.cacheExtent,
168189
slivers: [

lib/layout/list_view.dart

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:ensemble/action/haptic_action.dart';
22
import 'package:ensemble/framework/action.dart';
33
import 'package:ensemble/framework/error_handling.dart';
4+
import 'package:ensemble/framework/event.dart';
45
import 'package:ensemble/framework/scope.dart';
56
import 'package:ensemble/framework/studio/studio_debugger.dart';
67
import 'package:ensemble/framework/view/data_scope_widget.dart';
@@ -79,6 +80,8 @@ class ListView extends StatefulWidget
7980
_controller.hasReachedMax = Utils.getBool(value, fallback: false),
8081
'loadingWidget': (value) => _controller.loadingWidget = value,
8182
'data': (value) => _controller.itemTemplate?.data = value,
83+
'onScroll': (value) =>
84+
controller.onScroll = EnsembleAction.fromYaml(value, initiator: this),
8285
};
8386
}
8487

@@ -113,6 +116,7 @@ class ListViewController extends BoxLayoutController {
113116
bool hasReachedMax = false;
114117

115118
ListViewState? widgetState;
119+
EnsembleAction? onScroll;
116120

117121
void _bind(ListViewState state) {
118122
widgetState = state;
@@ -177,6 +181,15 @@ class ListViewState extends WidgetState<ListView>
177181
isLoading: showLoading,
178182
onFetchData: _fetchData,
179183
hasReachedMax: widget._controller.hasReachedMax,
184+
onScroll: (pixel) {
185+
if (widget._controller.onScroll != null) {
186+
ScreenController().executeAction(
187+
context,
188+
widget._controller.onScroll!,
189+
event: EnsembleEvent(null, data: {'pixel': pixel}),
190+
);
191+
}
192+
},
180193
scrollController: (footerScope != null &&
181194
footerScope.isColumnScrollableAndRoot(context))
182195
? null

lib/module/auth_module.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class AuthModuleStub implements AuthModule {
1616
GetIt.I.registerFactory<SignInWithAuth0>(() => const SignInWithAuth0Stub());
1717
GetIt.I.registerSingleton<TokenManager>(TokenManagerStub());
1818
GetIt.I.registerFactory<OAuthController>(() => OAuthControllerStub());
19-
19+
GetIt.I.registerFactory<SignInAnonymous>(() => SignInAnonymousStub());
2020
// note that we don't inject AuthContextManagerStub(), since its presence
2121
// will prevent data_context to load
2222
}

lib/provider.dart

+13-7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class RemoteDefinitionProvider extends DefinitionProvider {
132132
: super(i18nProps, cacheEnabled: cacheEnabled);
133133
final String path;
134134
final String appHome;
135+
UserAppConfig? appConfig;
135136
FlutterI18nDelegate? _i18nDelegate;
136137

137138
@override
@@ -172,12 +173,19 @@ class RemoteDefinitionProvider extends DefinitionProvider {
172173

173174
@override
174175
Future<AppBundle> getAppBundle({bool? bypassCache = false}) async {
176+
final env = await _readYamlFile('appConfig.yaml');
177+
if (env != null) {
178+
appConfig = UserAppConfig(
179+
baseUrl: path,
180+
envVariables: env as Map<String, dynamic>,
181+
);
182+
}
175183
return AppBundle(
176-
theme: await _readFile('theme.ensemble'),
177-
resources: await _readFile('resources.ensemble'));
184+
theme: await _readYamlFile('theme.ensemble'),
185+
resources: await _readYamlFile('resources.ensemble'));
178186
}
179187

180-
Future<YamlMap?> _readFile(String file) async {
188+
Future<YamlMap?> _readYamlFile(String file) async {
181189
try {
182190
http.Response response = await http.get(Uri.parse(path + file));
183191
if (response.statusCode == 200) {
@@ -189,16 +197,14 @@ class RemoteDefinitionProvider extends DefinitionProvider {
189197
return null;
190198
}
191199

192-
// TODO: to be implemented
193200
@override
194201
UserAppConfig? getAppConfig() {
195-
return null;
202+
return appConfig;
196203
}
197204

198-
// TODO: to be implemented
199205
@override
200206
Map<String, String> getSecrets() {
201-
return <String, String>{};
207+
return dotenv.env;
202208
}
203209
}
204210

lib/screen_controller.dart

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import 'package:ensemble/layout/ensemble_page_route.dart';
3434
import 'package:ensemble/page_model.dart';
3535
import 'package:ensemble/util/notification_utils.dart';
3636
import 'package:ensemble/util/utils.dart';
37+
import 'package:ensemble/widget/stub_widgets.dart';
3738
import 'package:ensemble_ts_interpreter/invokables/context.dart';
3839
import 'package:ensemble_ts_interpreter/parser/newjs_interpreter.dart';
3940
import 'package:flutter/foundation.dart';
@@ -465,6 +466,8 @@ class ScreenController {
465466
scopeManager: scopeManager);
466467
} else if (action is FilePickerAction) {
467468
GetIt.I<FileManager>().pickFiles(context, action, scopeManager);
469+
} else if (action is SignInAnonymousAction) {
470+
GetIt.I<SignInAnonymous>().signInAnonymously(context, action: action);
468471
} else if (action is WalletConnectAction) {
469472
// TODO store session: WalletConnectSession? session = await sessionStorage.getSession();
470473

lib/widget/stub_widgets.dart

+16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import 'package:ensemble/framework/action.dart';
12
import 'package:ensemble/framework/error_handling.dart';
3+
import 'package:ensemble/framework/scope.dart';
24
import 'package:flutter/cupertino.dart';
35

46
class StubWidget extends StatelessWidget {
@@ -52,3 +54,17 @@ abstract class SignInWithAuth0 {
5254
class SignInWithAuth0Stub extends StubWidget implements SignInWithAuth0 {
5355
const SignInWithAuth0Stub({super.key}) : super(moduleName: 'Auth');
5456
}
57+
58+
abstract class SignInAnonymous {
59+
Future<void> signInAnonymously(BuildContext context,
60+
{required SignInAnonymousAction action});
61+
}
62+
63+
class SignInAnonymousStub implements SignInAnonymous {
64+
@override
65+
Future<void> signInAnonymously(BuildContext context,
66+
{required SignInAnonymousAction action}) {
67+
throw ConfigError(
68+
"Auth is not enabled. Please review the Ensemble documentation.");
69+
}
70+
}

0 commit comments

Comments
 (0)