Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Realtime Channel history #52

Merged
merged 20 commits into from
Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,30 @@ await channel.publish(messages: [
ably.Message()..name="event1"..data = {"hello": "world"}
]);
```
Get realtime history

```dart
void getHistory([ably.RealtimeHistoryParams params]) async {
var result = await channel.history(params);

var messages = result.items; //get messages
var hasNextPage = result.hasNext(); //tells whether there are more results
if(hasNextPage){
result = await result.next(); //fetches next page results
messages = result.items;
}
if(!hasNextPage){
result = await result.first(); //fetches first page results
messages = result.items;
}
}

// history with default params
getHistory();

// sorted and filtered history
getHistory(ably.RealtimeHistoryParams(direction: 'forwards', limit: 10));
```

## Caveats

Expand Down
29 changes: 29 additions & 0 deletions android/src/main/java/io/ably/flutter/plugin/AblyMessageCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ T decode(Map<String, Object> jsonMap) {
new CodecPair<>(self::encodePaginatedResult, null));
put(PlatformConstants.CodecTypes.restHistoryParams,
new CodecPair<>(null, self::decodeRestHistoryParams));
put(PlatformConstants.CodecTypes.realtimeHistoryParams,
new CodecPair<>(null, self::decodeRealtimeHistoryParams));
put(PlatformConstants.CodecTypes.errorInfo,
new CodecPair<>(self::encodeErrorInfo, null));
put(PlatformConstants.CodecTypes.message,
Expand Down Expand Up @@ -324,6 +326,33 @@ private Param[] decodeRestHistoryParams(Map<String, Object> jsonMap) {
return params;
}

private Param[] decodeRealtimeHistoryParams(Map<String, Object> jsonMap) {
if (jsonMap == null) return null;
Param[] params = new Param[jsonMap.size()];
int index = 0;
final Object start = jsonMap.get(PlatformConstants.TxRealtimeHistoryParams.start);
final Object end = jsonMap.get(PlatformConstants.TxRealtimeHistoryParams.end);
final Object limit = jsonMap.get(PlatformConstants.TxRealtimeHistoryParams.limit);
final Object direction = jsonMap.get(PlatformConstants.TxRealtimeHistoryParams.direction);
final Object untilAttach = jsonMap.get(PlatformConstants.TxRealtimeHistoryParams.untilAttach);
if(start!=null) {
params[index++] = new Param(PlatformConstants.TxRealtimeHistoryParams.start, readValueAsLong(start));
}
if(end!=null) {
params[index++] = new Param(PlatformConstants.TxRealtimeHistoryParams.end, readValueAsLong(end));
}
if(limit!=null) {
params[index++] = new Param(PlatformConstants.TxRealtimeHistoryParams.limit, (Integer) limit);
}
if(direction!=null) {
params[index++] = new Param(PlatformConstants.TxRealtimeHistoryParams.direction, (String) direction);
}
if(untilAttach!=null) {
params[index] = new Param(PlatformConstants.TxRealtimeHistoryParams.untilAttach, (boolean) untilAttach);
}
return params;
}

private Message decodeChannelMessage(Map<String, Object> jsonMap) {
if (jsonMap == null) return null;
final Message o = new Message();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ private AblyMethodCallHandler(final MethodChannel channel, final OnHotRestart li

// history
_map.put(PlatformConstants.PlatformMethod.restHistory, this::getRestHistory);
_map.put(PlatformConstants.PlatformMethod.realtimeHistory, this::getRealtimeHistory);

// paginated results
_map.put(PlatformConstants.PlatformMethod.nextPage, this::getNextPage);
Expand Down Expand Up @@ -223,9 +224,9 @@ private void getRestHistory(@NonNull MethodCall call, @NonNull MethodChannel.Res
final AblyFlutterMessage message = (AblyFlutterMessage) call.arguments;
this.<AblyFlutterMessage<Map<String, Object>>>ablyDo(message, (ablyLibrary, messageData) -> {
final Map<String, Object> map = messageData.message;
final String channelName = (String) map.get(PlatformConstants.TxRestHistoryArguments.channelName);
Param[] params = (Param[]) map.get(PlatformConstants.TxRestHistoryArguments.params);
if(params == null){
final String channelName = (String) map.get(PlatformConstants.TxTransportKeys.channelName);
Param[] params = (Param[]) map.get(PlatformConstants.TxTransportKeys.params);
if (params == null) {
params = new Param[0];
}
ablyLibrary
Expand Down Expand Up @@ -426,6 +427,22 @@ public void onError(ErrorInfo reason) {
});
}

private void getRealtimeHistory(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
final AblyFlutterMessage message = (AblyFlutterMessage) call.arguments;
this.<AblyFlutterMessage<Map<String, Object>>>ablyDo(message, (ablyLibrary, messageData) -> {
final Map<String, Object> map = messageData.message;
final String channelName = (String) map.get(PlatformConstants.TxTransportKeys.channelName);
Param[] params = (Param[]) map.get(PlatformConstants.TxTransportKeys.params);
if (params == null) {
params = new Param[0];
}
ablyLibrary
.getRealtime(messageData.handle)
.channels.get(channelName)
.historyAsync(params, this.paginatedResponseHandler(result, null));
});
}

private void closeRealtime(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
final AblyFlutterMessage message = (AblyFlutterMessage) call.arguments;
this.<Number>ablyDo(message, (ablyLibrary, realtimeHandle) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ static final public class CodecTypes {
public static final byte tokenRequest = (byte) 134;
public static final byte paginatedResult = (byte) 135;
public static final byte restHistoryParams = (byte) 136;
public static final byte realtimeHistoryParams = (byte) 137;
public static final byte errorInfo = (byte) 144;
public static final byte connectionStateChange = (byte) 201;
public static final byte channelStateChange = (byte) 202;
Expand Down Expand Up @@ -47,6 +48,11 @@ static final public class PlatformMethod {
public static final String firstPage = "firstPage";
}

static final public class TxTransportKeys {
public static final String channelName = "channelName";
public static final String params = "params";
}

static final public class TxAblyMessage {
public static final String registrationHandle = "registrationHandle";
public static final String type = "type";
Expand Down Expand Up @@ -179,16 +185,19 @@ static final public class TxPaginatedResult {
public static final String hasNext = "hasNext";
}

static final public class TxRestHistoryArguments {
public static final String channelName = "channelName";
public static final String params = "params";
static final public class TxRestHistoryParams {
public static final String start = "start";
public static final String end = "end";
public static final String direction = "direction";
public static final String limit = "limit";
}

static final public class TxRestHistoryParams {
static final public class TxRealtimeHistoryParams {
public static final String start = "start";
public static final String end = "end";
public static final String direction = "direction";
public static final String limit = "limit";
public static final String untilAttach = "untilAttach";
}

}
17 changes: 14 additions & 3 deletions bin/codegencontext.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const List<Map<String, dynamic>> _types = [
{'name': 'tokenRequest', 'value': 134},
{'name': 'paginatedResult', 'value': 135},
{'name': 'restHistoryParams', 'value': 136},
{'name': 'realtimeHistoryParams', 'value': 137},
{'name': 'errorInfo', 'value': 144},

// Events
Expand Down Expand Up @@ -79,6 +80,10 @@ const List<Map<String, dynamic>> _platformMethods = [
];

const List<Map<String, dynamic>> _objects = [
{
'name': 'TransportKeys',
'properties': <String>['channelName', 'params']
},
{
'name': 'AblyMessage',
'properties': <String>['registrationHandle', 'type', 'message']
Expand Down Expand Up @@ -214,16 +219,22 @@ const List<Map<String, dynamic>> _objects = [
'properties': <String>['items', 'type', 'hasNext']
},
{
'name': 'RestHistoryArguments',
'properties': <String>['channelName', 'params']
'name': 'RestHistoryParams',
'properties': <String>[
'start',
'end',
'direction',
'limit',
]
},
{
'name': 'RestHistoryParams',
'name': 'RealtimeHistoryParams',
'properties': <String>[
'start',
'end',
'direction',
'limit',
'untilAttach',
]
}
];
Expand Down
130 changes: 97 additions & 33 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class _MyAppState extends State<MyApp> {
StreamSubscription<ably.Message> _channelMessageSubscription;
ably.Message channelMessage;
ably.PaginatedResult<ably.Message> _restHistory;
ably.PaginatedResult<ably.Message> _realtimeHistory;

//Storing different message types here to be publishable
List<dynamic> messagesToPublish = [
Expand Down Expand Up @@ -447,44 +448,101 @@ class _MyAppState extends State<MyApp> {
int msgCounter = 1;

Widget sendRestMessage() => FlatButton(
onPressed: () async {
print('Sending rest message');
try {
await _rest.channels
.get('test')
.publish(name: 'Hello', data: 'Flutter $msgCounter');
print('Rest message sent.');
setState(() {
++msgCounter;
});
} on ably.AblyException catch (e) {
print('Rest message sending failed:: $e :: ${e.errorInfo}');
}
},
onPressed: (_rest == null)
? null
: () async {
print('Sending rest message');
try {
await _rest.channels
.get('test')
.publish(name: 'Hello', data: 'Flutter $msgCounter');
print('Rest message sent.');
setState(() {
++msgCounter;
});
} on ably.AblyException catch (e) {
print('Rest message sending failed:: $e :: ${e.errorInfo}');
}
},
color: Colors.yellow,
child: const Text('Publish'),
);

Widget getRestChannelHistory() => FlatButton(
onPressed: () async {
final next = _restHistory?.hasNext() ?? false;
print('Rest history: getting ${next ? 'next' : 'first'} page');
try {
if (_restHistory == null || _restHistory.items.isEmpty) {
final result = await _rest.channels.get('test').history(
ably.RestHistoryParams(direction: 'forwards', limit: 10),
);
_restHistory = result;
} else if (next) {
_restHistory = await _restHistory.next();
} else {
_restHistory = await _restHistory.first();
}
setState(() {});
} on ably.AblyException catch (e) {
print('failed to get history:: $e :: ${e.errorInfo}');
}
},
onPressed: (_rest == null)
? null
: () async {
final next = _restHistory?.hasNext() ?? false;
print('Rest history: getting ${next ? 'next' : 'first'} page');
try {
if (_restHistory == null || _restHistory.items.isEmpty) {
final result = await _rest.channels.get('test').history(
ably.RestHistoryParams(
direction: 'forwards', limit: 10));
_restHistory = result;
} else if (next) {
_restHistory = await _restHistory.next();
} else {
_restHistory = await _restHistory.first();
}
setState(() {});
} on ably.AblyException catch (e) {
print('failed to get history:: $e :: ${e.errorInfo}');
}
},
onLongPress: (_rest == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Is this behaviour obvious? Maybe I don't fully understand the context (I've not loaded the app in a while, least not for this branch), but I would usually expect a long-press to first present a menu or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QuintinWillison long press is not intuitive, I had a plan to enhance this, but primary focus was not on example.

? null
: () async {
final result = await _rest.channels.get('test').history(
ably.RestHistoryParams(direction: 'forwards', limit: 10));
setState(() {
_restHistory = result;
});
},
color: Colors.yellow,
child: const Text('Get history'),
);

Widget getRealtimeChannelHistory() => FlatButton(
onPressed: (_realtime == null)
? null
: () async {
final next = _realtimeHistory?.hasNext() ?? false;
print('Rest history: getting ${next ? 'next' : 'first'} page');
try {
if (_realtimeHistory == null ||
_realtimeHistory.items.isEmpty) {
final result =
await _realtime.channels.get('test-channel').history(
ably.RealtimeHistoryParams(
direction: 'backwards',
limit: 10,
untilAttach: true,
),
);
_realtimeHistory = result;
} else if (next) {
_realtimeHistory = await _realtimeHistory.next();
} else {
_realtimeHistory = await _realtimeHistory.first();
}
setState(() {});
} on ably.AblyException catch (e) {
print('failed to get history:: $e :: ${e.errorInfo}');
}
},
onLongPress: (_realtime == null)
? null
: () async {
final result =
await _realtime.channels.get('test-channel').history(
ably.RealtimeHistoryParams(
direction: 'forwards', limit: 10),
);
setState(() {
_realtimeHistory = result;
});
},
color: Colors.yellow,
child: const Text('Get history'),
);
Expand Down Expand Up @@ -539,6 +597,12 @@ class _MyAppState extends State<MyApp> {
),
Text('Message from channel: ${channelMessage?.data ?? '-'}'),
createChannelPublishButton(),
getRealtimeChannelHistory(),
const Text('History'),
..._realtimeHistory?.items
?.map((m) => Text('${m.name}:${m.data?.toString()}'))
?.toList() ??
[],
const Divider(),
createRestButton(),
Text('Rest: '
Expand Down
Loading