regenerate stream endpoints

This commit is contained in:
tk 2025-08-16 18:39:33 +02:00
parent 9de633feb9
commit 33c98019bb
9 changed files with 177 additions and 198 deletions

View file

@ -23,21 +23,6 @@ class EndpointQuote extends _i1.EndpointRef {
@override
String get name => 'quote';
_i2.Future<void> updateQuote(_i3.Quote quote) =>
caller.callServerEndpoint<void>(
'quote',
'updateQuote',
{'quote': quote},
);
_i2.Stream<_i3.Quote> quoteUpdates() =>
caller.callStreamingServerEndpoint<_i2.Stream<_i3.Quote>, _i3.Quote>(
'quote',
'quoteUpdates',
{},
{},
);
_i2.Future<_i3.Quote> createQuote(_i4.CreateQuoteRequest req) =>
caller.callServerEndpoint<_i3.Quote>(
'quote',
@ -45,18 +30,18 @@ class EndpointQuote extends _i1.EndpointRef {
{'req': req},
);
_i2.Future<_i3.Quote> getQuoteById(int id) =>
caller.callServerEndpoint<_i3.Quote>(
_i2.Future<void> updateQuote(_i3.Quote quote) =>
caller.callServerEndpoint<void>(
'quote',
'getQuoteById',
{'id': id},
'updateQuote',
{'quote': quote},
);
_i2.Future<List<_i3.Quote>> getAllQuotes() =>
_i2.Future<List<_i3.Quote>> getAllQuotes({required int limit}) =>
caller.callServerEndpoint<List<_i3.Quote>>(
'quote',
'getAllQuotes',
{},
{'limit': limit},
);
}

View file

@ -3,7 +3,7 @@ import 'package:wien_talks_flutter/helper/funmap_mgr.dart';
import 'package:wien_talks_flutter/helper/go_router.dart';
void main() {
FunmapMgr();
FunmapMgr().configure();
runApp(const MyApp());
}

View file

@ -17,7 +17,7 @@ class NewsScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
HeadingText(text: "Latest news"),
QuoteList(),
ShowLatestNewsWidget(),
SizedBox(
height: 30,
),

View file

@ -1,27 +1,87 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:wien_talks_flutter/widgets/heading_text.dart';
import 'package:wien_talks_client/wien_talks_client.dart';
import 'package:wien_talks_flutter/helper/funmap_mgr.dart';
import 'helper/funmap_mgr.dart';
class ShowLatestNewsWidget extends StatelessWidget {
class ShowLatestNewsWidget extends StatefulWidget {
const ShowLatestNewsWidget({super.key});
@override
State<ShowLatestNewsWidget> createState() => _ShowLatestNewsWidgetState();
}
class _ShowLatestNewsWidgetState extends State<ShowLatestNewsWidget> {
final _controller = StreamController<List<Quote>>.broadcast();
Timer? _timer;
@override
void initState() {
super.initState();
_reload();
_timer = Timer.periodic(const Duration(seconds: 30), (_) => _reload());
}
Future<void> _reload() async {
try {
final quotes = await FunmapMgr().client.quote.getAllQuotes(limit: 200);
_controller.add(quotes);
} catch (e, st) {
_controller.addError(e, st);
}
}
@override
void dispose() {
_timer?.cancel();
_controller.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FunmapMgr().client.quote.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
return RefreshIndicator(
onRefresh: _reload,
child: StreamBuilder<List<Quote>>(
stream: _controller.stream,
initialData: const <Quote>[],
builder: (context, snap) {
if (snap.hasError) {
return ListView(
children: [
HeadingText(text: "Latest news"),
if (snapshot.hasError) Text('Error: ${snapshot.error}'),
Text(snapshot.data ?? "Be the first to submit amazing news!",
style: TextStyle(
fontSize: 20,
color: Theme.of(context).colorScheme.error)),
Padding(
padding: const EdgeInsets.all(16),
child: Text('Error: ${snap.error}'),
),
],
);
});
}
final quotes = snap.data ?? const <Quote>[];
if (quotes.isEmpty) {
return ListView(
children: const [
Padding(
padding: EdgeInsets.all(16),
child: Text('No quotes yet. Pull to refresh.'),
),
],
);
}
return ListView.separated(
itemCount: quotes.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, i) {
final q = quotes[i];
return ListTile(
title: Text(q.text),
subtitle: Text([
if ((q.authorName ?? '').isNotEmpty) q.authorName!,
q.createdAt.toLocal().toString(),
].where((e) => e.isNotEmpty).join(' · ')),
);
},
);
},
),
);
}
}

View file

@ -11,9 +11,9 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod/serverpod.dart' as _i1;
import '../quotes/quotes_endpoint.dart' as _i2;
import 'package:wien_talks_server/src/generated/quotes/quote.dart' as _i3;
import 'package:wien_talks_server/src/generated/quotes/create_quote.dart'
as _i4;
as _i3;
import 'package:wien_talks_server/src/generated/quotes/quote.dart' as _i4;
import 'package:serverpod_auth_server/serverpod_auth_server.dart' as _i5;
class Endpoints extends _i1.EndpointDispatch {
@ -31,30 +31,12 @@ class Endpoints extends _i1.EndpointDispatch {
name: 'quote',
endpoint: endpoints['quote']!,
methodConnectors: {
'updateQuote': _i1.MethodConnector(
name: 'updateQuote',
params: {
'quote': _i1.ParameterDescription(
name: 'quote',
type: _i1.getType<_i3.Quote>(),
nullable: false,
)
},
call: (
_i1.Session session,
Map<String, dynamic> params,
) async =>
(endpoints['quote'] as _i2.QuoteEndpoint).updateQuote(
session,
params['quote'],
),
),
'createQuote': _i1.MethodConnector(
name: 'createQuote',
params: {
'req': _i1.ParameterDescription(
name: 'req',
type: _i1.getType<_i4.CreateQuoteRequest>(),
type: _i1.getType<_i3.CreateQuoteRequest>(),
nullable: false,
)
},
@ -67,11 +49,29 @@ class Endpoints extends _i1.EndpointDispatch {
params['req'],
),
),
'getQuoteById': _i1.MethodConnector(
name: 'getQuoteById',
'updateQuote': _i1.MethodConnector(
name: 'updateQuote',
params: {
'id': _i1.ParameterDescription(
name: 'id',
'quote': _i1.ParameterDescription(
name: 'quote',
type: _i1.getType<_i4.Quote>(),
nullable: false,
)
},
call: (
_i1.Session session,
Map<String, dynamic> params,
) async =>
(endpoints['quote'] as _i2.QuoteEndpoint).updateQuote(
session,
params['quote'],
),
),
'getAllQuotes': _i1.MethodConnector(
name: 'getAllQuotes',
params: {
'limit': _i1.ParameterDescription(
name: 'limit',
type: _i1.getType<int>(),
nullable: false,
)
@ -80,32 +80,11 @@ class Endpoints extends _i1.EndpointDispatch {
_i1.Session session,
Map<String, dynamic> params,
) async =>
(endpoints['quote'] as _i2.QuoteEndpoint).getQuoteById(
(endpoints['quote'] as _i2.QuoteEndpoint).getAllQuotes(
session,
params['id'],
limit: params['limit'],
),
),
'getAllQuotes': _i1.MethodConnector(
name: 'getAllQuotes',
params: {},
call: (
_i1.Session session,
Map<String, dynamic> params,
) async =>
(endpoints['quote'] as _i2.QuoteEndpoint).getAllQuotes(session),
),
'quoteUpdates': _i1.MethodStreamConnector(
name: 'quoteUpdates',
params: {},
streamParams: {},
returnType: _i1.MethodStreamReturnType.streamType,
call: (
_i1.Session session,
Map<String, dynamic> params,
Map<String, Stream> streamParams,
) =>
(endpoints['quote'] as _i2.QuoteEndpoint).quoteUpdates(session),
),
},
);
modules['serverpod_auth'] = _i5.Endpoints()..initializeEndpoints(server);

View file

@ -1,6 +1,4 @@
quote:
- updateQuote:
- quoteUpdates:
- createQuote:
- getQuoteById:
- updateQuote:
- getAllQuotes:

View file

@ -1,34 +1,22 @@
import 'dart:math';
// lib/src/endpoints/quote_endpoint.dart
import 'dart:async';
import 'package:serverpod/serverpod.dart';
import 'package:wien_talks_server/src/generated/protocol.dart';
import 'package:wien_talks_server/src/quotes/quote_util.dart';
class QuoteEndpoint extends Endpoint {
class ShowLatestNewsWidget extends Endpoint {
static const _channelQuoteUpdates = 'quote-updates';
Future<void> updateQuote(Session session, Quote quote) async {
await Quote.db.updateRow(session, quote);
await session.messages.postMessage(_channelQuoteUpdates, quote);
}
Stream<Quote> quoteUpdates(Session session) async* {
var updateStream =
session.messages.createStream<Quote>(_channelQuoteUpdates);
await for (var quote in updateStream) {
yield quote;
}
}
Future<Quote> createQuote(Session session, CreateQuoteRequest req) async {
final authInfo = await session.authenticated;
final userId = Random().nextInt(100);
final userId = authInfo?.userId;
String text = validateQuote(req);
final text = validateQuote(req);
final quote = Quote(
userId: userId,
final toInsert = Quote(
id: 0,
userId: userId ?? 12,
text: text,
authorName: req.authorName,
lat: req.lat,
@ -39,36 +27,61 @@ class QuoteEndpoint extends Endpoint {
downvotes: 0,
);
final inserted = await session.db.insertRow<Quote>(quote);
final inserted = await session.db.insertRow<Quote>(toInsert);
await session.messages.postMessage(_channelQuoteUpdates, inserted);
return inserted;
}
Future<Quote> getQuoteById(Session session, int id) async {
final quote = await Quote.db.findById(session, id);
if (quote != null) {
return quote;
Future<void> updateQuote(Session session, Quote quote) async {
await Quote.db.updateRow(session, quote);
await session.messages.postMessage(_channelQuoteUpdates, quote);
}
throw Exception('Quote not found');
Future<List<Quote>> getAllQuotes(Session session, {int limit = 200}) async {
final quoteList = await Quote.db.find(session);
return quoteList;
}
Future<List<Quote>> getAllQuotes(Session session) async {
final quotes = await Quote.db.find(session);
return quotes;
}
Stream streamAllQuotes(
StreamingSession session, {
int limit = 200,
}) async* {
Future<Stream<Quote>> streamAllQuotes(StreamingSession session,
{int limit = 200}) async {
if (limit <= 0 || limit > 500) limit = 200;
final quoteStream = session.messages.createStream<Quote>('quotes');
final controller = StreamController<Quote>();
final live = session.messages.createStream<Quote>(_channelQuoteUpdates);
final liveSub = live.listen(
(q) {
if (q.visibility == 0) controller.add(q);
},
onError: controller.addError,
onDone: () {
if (!controller.isClosed) controller.close();
},
cancelOnError: false,
);
await for (final Quote quote in quoteStream) {
yield quote;
}
() async* {
try {
final snapshot = await Quote.db.find(
session,
where: (t) => t.visibility.equals(0),
orderBy: (t) => t.createdAt,
orderDescending: true,
limit: limit,
);
for (final q in snapshot.reversed) {
controller.add(q);
}
} catch (e, st) {
controller.addError(e, st);
}
}();
await session.close().then((_) async {
await liveSub.cancel();
await controller.close();
});
return controller.stream;
}
}

View file

@ -130,64 +130,6 @@ class _QuoteEndpoint {
final _i2.SerializationManager _serializationManager;
_i3.Future<void> updateQuote(
_i1.TestSessionBuilder sessionBuilder,
_i4.Quote quote,
) async {
return _i1.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild(
endpoint: 'quote',
method: 'updateQuote',
);
try {
var _localCallContext = await _endpointDispatch.getMethodCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'quote',
methodName: 'updateQuote',
parameters: _i1.testObjectToJson({'quote': quote}),
serializationManager: _serializationManager,
);
var _localReturnValue = await (_localCallContext.method.call(
_localUniqueSession,
_localCallContext.arguments,
) as _i3.Future<void>);
return _localReturnValue;
} finally {
await _localUniqueSession.close();
}
});
}
_i3.Stream<_i4.Quote> quoteUpdates(_i1.TestSessionBuilder sessionBuilder) {
var _localTestStreamManager = _i1.TestStreamManager<_i4.Quote>();
_i1.callStreamFunctionAndHandleExceptions(
() async {
var _localUniqueSession =
(sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild(
endpoint: 'quote',
method: 'quoteUpdates',
);
var _localCallContext =
await _endpointDispatch.getMethodStreamCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'quote',
methodName: 'quoteUpdates',
arguments: {},
requestedInputStreams: [],
serializationManager: _serializationManager,
);
await _localTestStreamManager.callStreamMethod(
_localCallContext,
_localUniqueSession,
{},
);
},
_localTestStreamManager.outputStreamController,
);
return _localTestStreamManager.outputStreamController.stream;
}
_i3.Future<_i4.Quote> createQuote(
_i1.TestSessionBuilder sessionBuilder,
_i5.CreateQuoteRequest req,
@ -217,28 +159,28 @@ class _QuoteEndpoint {
});
}
_i3.Future<_i4.Quote> getQuoteById(
_i3.Future<void> updateQuote(
_i1.TestSessionBuilder sessionBuilder,
int id,
_i4.Quote quote,
) async {
return _i1.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild(
endpoint: 'quote',
method: 'getQuoteById',
method: 'updateQuote',
);
try {
var _localCallContext = await _endpointDispatch.getMethodCallContext(
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'quote',
methodName: 'getQuoteById',
parameters: _i1.testObjectToJson({'id': id}),
methodName: 'updateQuote',
parameters: _i1.testObjectToJson({'quote': quote}),
serializationManager: _serializationManager,
);
var _localReturnValue = await (_localCallContext.method.call(
_localUniqueSession,
_localCallContext.arguments,
) as _i3.Future<_i4.Quote>);
) as _i3.Future<void>);
return _localReturnValue;
} finally {
await _localUniqueSession.close();
@ -247,7 +189,9 @@ class _QuoteEndpoint {
}
_i3.Future<List<_i4.Quote>> getAllQuotes(
_i1.TestSessionBuilder sessionBuilder) async {
_i1.TestSessionBuilder sessionBuilder, {
required int limit,
}) async {
return _i1.callAwaitableFunctionAndHandleExceptions(() async {
var _localUniqueSession =
(sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild(
@ -259,7 +203,7 @@ class _QuoteEndpoint {
createSessionCallback: (_) => _localUniqueSession,
endpointPath: 'quote',
methodName: 'getAllQuotes',
parameters: _i1.testObjectToJson({}),
parameters: _i1.testObjectToJson({'limit': limit}),
serializationManager: _serializationManager,
);
var _localReturnValue = await (_localCallContext.method.call(