rime

Last updated:

0 purchases

rime Image
rime Images
Add to Cart

Description:

rime

Rime SDK #
Rime is a messaging SDK that allows you to more easily integrate the PubNub real-time message system into your application.
Overview #
The goal of this package is to make it easy to implement PubNub's rest API.
This package helps provide state functionality for PubNub within Flutter application's. This package allows developers
to focus on design rather then Software Requirements when managing chat application's.
The plug and play nature of Rime ensures that the developer does not have to deal with the synchronization of chat data application wide. Rime also formats Pubnub data into user relavent data in the provided models.
Authentication and Users
❗❗❗ IMPORTANT ❗❗❗
Rime does not provide authentication functionality, that is entirely up to the developer to implement. Rime assumes that each user has a unique userId that can be provided to access a user's Rime account.
Jump to: #

Installation
Importing
Dart Version Support
Set Up

Generate Pubnub Keyset
Initialize Rime
Verifying


Rime Structure

Architectural Overview
Rime Bloc
Rime API
Rime Repository

Initialization
Channel Groups
Listening to Subscriptions




Usage

Models

Rime Channel
Rime Message

Rime Message Encoding
Rime Message Decoding
Using Rime Message




Rime API
Rime State
Rime Events

Initialize Event
Get Channels Event
Create Channel Event
Send Message Event
Delete Event
Leave Event
Store Event
Clear Event


Retreiving Channel History



Installation #
To add the SDK to your Dart or Flutter project, add rime as a dependency to your pubspec.yaml.
dependencies:
rime: ^0.1.0
copied to clipboard
After adding the dependency to pubspec.yaml, run pub get command in the root directory of your project.
Importing #
After installing the Rime package import it in your application.
import 'package:rime/rime.dart';
copied to clipboard
Dart Versions #

Dart 2: >= 2.7.0

Set Up #
To sucessfuly integrate rime into your application the following steps must be completed.
1) Generate Pubnub Keyset #
Rime requires you to provision thier own Pubnub instance. The SDK adds structure to this instance through its provided functionality.
Start by logging into to the Pubnub admin console at https://www.pubnub.com and sign up to retreive a publish and subscribe keyset.
Create a .env file in your root directory (Same directory as pubspec.yaml) and copy and paste your publish and subscribe keys there
#Pubnub
RIME_PUB_KEY=<Pubnub Publish Key>
RIME_SUB_KEY=<Pubnub Subscribe Key>
copied to clipboard
Add the following line to you pubspec.yaml to ensure that the .env file is correctly imported
flutter:
assets:
- .env

copied to clipboard
After adding the dependency to pubspec.yaml, run pub get command in the root directory of your project.
2) Initialize Rime #
Within the main of your application, make sure you initialize Rime by passing in the environment variables. This will extract the Pubnub publish and subscribe to store for Rime to connect to a Pubnub instance. Optionally you can pass in any custom functions you want rime to run interally by defining a RimeDeveloperFunctions object.
import 'package:rime/rime.dart';

import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;

void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();

print('Initializing');
await DotEnv.load(fileName: '.env');
await Rime.initialize(DotEnv.env, RimeDeveloperFunctions(
//Optional
));
print('Initialized');

...
//run app
}
copied to clipboard
3) Verfying #
The following snippet can be run to verify whether Rime is initialized.
print(Rime.INITIALIZED);
copied to clipboard
If this statement is true then the rime sdk is correctly set up and can be used.
Stucture #
Pubnub #
Rime SDK heavy relies on the underlying funcationality of Pubnub to provide a serverless real-time SDK. For a complete frame of refrence on underlying Rime functionality when developing please refrence the pubnub Flutter client and Pubnub's official documentation.
Architectural Overview #
Rime SDK is composed of 3 core elements RimeAPI, RimeRepository, and RimeBloc. These work together to provide publish and subscribe functionality from Pubnub along with the management of Rime channel metadata.


Represents the relationship between components

RimeBloc #
RimeBloc is a Singleton Global State Manager for Rime, it manages application wide buinsess logic for channels; allowing components to directly subscribe to a stream of up-to-date channel data. The RimeBloc acts as the user's primary interface to communicate with Rime and the underlying Pubnub interface safely.
The RimeBloc object is an auto-instantiated singleton object that is generated using the the get_it package. Use the following code to access the object from anywhere in your application.
RimeBloc();
copied to clipboard
On first access the object is automaically created with a non-initialized state. In this state the RimeBloc.state will not contain any channels. It must first be initialized by the developer using a unique userId provided on user authentication.
When initialized the user can push events to the RimeBloc to manipulate, create, and leave channels relative the provided UserId.
For more information on RimeEvents and RimeState see Usage.
Implementation
The RimeBloc is implemented using the flutter_bloc package. Please refrence their online documentation for information on how to correctly interface with RimeBloc using Bloc.
RimeAPI #
The RimeAPI is a set of functions that directly interface with the internal Pubnub client stored within RimeRepository.
Sending request directly through the API will successfully modify the PubNub instance with Rime attributes. However, using these methods will not make any local changes to the RimeState. The functions defined in this class are 1-to-1 with RimeEvents which do modify the state.
For more information on RimeAPI see Usage.
RimeRepository #
RimeRepository is a Singleton Storage Module that maintains the core Pubnub service. It is the root component of the Rime SDK and provides access to user subscription data, the client userId, and the live Pubnub client.
The RimeRepository object is an auto-instantiated singleton object that is generated using the the get_it package. Use the following code to access the object from anywhere in your application.
RimeRepository();
copied to clipboard
Initialization
Initializing a RimeRepository is a crucial step before using any Rime/Pubnub related functionality. The developer is responisble for providing a userId into this function. it is assumed that the Id provided is an unique key associated to an authenticated user. Rime will use the userId to create a UUID for the Pubnub client. This will be used to associate channels and messages with the user.
The following code initializes the RimeRepository for a user.
await RimeRepository().initializeRime('userId');
copied to clipboard

❗ Important ❗
The RimeRepository must be initialized before using the RimeAPI. This extends to any events that are dependent on RimeLiveState.
The RimeBloc automatically initializes the RimeRepository when it is initialized

Upon initilization the Pubnub client and the userId can be globally accessed.
//Accessing Pubnub client
RimeRepository().client

//Accessing the authenticated userId
RimeRepository().userId

copied to clipboard
👎 Avoid using the Pubnub client to directly manipulate the Pubnub instance as it may corrupt Rime encoded information.
Channel Groups
Rime manages the user's subscriptions by grouping channel subscriptions for a user into Pubnub channel groups. The rationale behind this is to limit the number of subscriptions managed by the Pubnub client. Rime allocates 10 channel groups to each user, each group can maintain up to 2000 subscriptions each.
When the RimeRepository is initiailized it automatically subscribes to all non-empty channel groups for a given user. The following can be used to accomplish this manually.
await RimeRepository().reset()
copied to clipboard
👍 The RimeAPI handles grouping and ungrouping channels.
For more information about Pubnub channel groups and client subscription limits please refer to the Pubnub official documentation
Listening to Subscriptions
The RimeRepository exposes an interface for binded components to listen to events directly from Rime channel subscriptions.
Binding
Components can bind to the RimeRepository by providing a unique binding key and a valid call back function. The callback will fire when the client receives any messages, actions, or events sent through Pubnub to the subscribed channels.
//Define callback
RimeCallBack callBack = (Envelope en){
...
}

//Bind to RimeRepository
RimeRepository().addListener('unique-key', callBack)
copied to clipboard
❗ The RimeBloc reserves the listener key "rime-bloc-listener" when it is initialized
Unbinding
When the event listener is not needed, components can unbind by inputting the unique key used to bind.
//Unbind to RimeRepository
RimeRepository().removeListener('unique-key')
copied to clipboard
👍 Saving the unique key in the component to ensure binding does not loose refrence.
👍 Unbinding when subscription is no longer needed in the component
👍 Unbinding on dispose() when the component is a StatefulWidget.
👎 Forgetting to close a listener
Usage #
Models #
Rime uses 3 models to encapsulate and format data from Pubnub: RimeChannel, RimeMessage, and RimeChannelMembership.
RimeChannel
A RimeChannel object is used to encapsulate information related to a Pubnub channel like the most recent message, user membership metadata, members in a channel.
👍 The RimeChannel object is Equatable.
Properties



Name
Type
Description




channel
String
The Pubnub channel Id associated to this channel.


groupId
String
The channel group this users channel membership is within.


title
String
The display title for the message. defaulted to null


subtitle
dynamic
The most recent message sent in the channel in the form of RimeMessage.enconde()


lastUpdated
int
The TikeToken value for the last message sent in the channel. Used in the Comparable interface to compare rime channels.


image
String
The image of the channel, used to store a network url of a hosted image. defaulted to null


isGroup
String
Determines if the channel is group or individual channel. Determined at creation.


readMap
Map<String, int>
A map containing channel members, refrenced by userId, mapped to the last message Timetoken read by the user.


membership
RimeChannelMembership
A RimeChannelMembership object containing the authenticated user's membership settings.


uuids
List<String>
A list of userId's associated to current members in the channel



RimeMessage
RimeMessage is an extention of Pubnub's BaseMessage. This model enfoces and encoding on the payload of sent BaseMessage's to include the authenticalted user's uuid and the type of the message.
Encoding
A RimeMessage is a JSON encoded set of properties.



Name
Type
Description




uuid
String
The userId for the sender of the message.


type
String
The type of the message. Allows you to determine the type of strategy to choose to decode the payload


payload
dynamic
Developer defined payload for a message. Format should be associated to the type of the message



Exsiting RimeMessages can be encoded using
//Encoding an exsisting RimeMessage
RimeMessage exsistingMessage;
Map<String, dynamic> encoding = exsistingMessage.encode();
copied to clipboard
However, if all encoding properties (uuid, type, and payload) are available and encoding can be generated using
//Encoding with exsisting properties
String uuid;
String type;
dynamic contentPayload
Map<String, dynamic> encoding = RimeMessage.toRimeMesageEncoding( uuid, type, contentPayload);
copied to clipboard
Decoding
A BaseMessage that is retrieved from Pubnub that contains an rimes a RimeMessage encoding within it's content has suffient information to be decoded into a RimeMessage object. Decoding removes the JSON nesting around the BaseMessage.content, extracting the type, uuid, and ensuring the content within the created message is the developer encoded message payload.
//Decoding from an exsisiting BaseMessage from Pubnub
BaseMessage receivedMessage;
RimeMessage rimeMessage = RimeMessage.fromBaseMessage(receivedMessage);

print(receivedMessage.content['payload'] == rimeMessage.content);
// True
copied to clipboard
Properties



Name
Type
Description




uuid
String
The userId for the sender of the message.


type
String
The type of the message. Allows you to determine the type of strategy to choose to decode the content


content
dynamic
Developer defined payload for a message. Format should be associated to the type of the message. Extracted from payload defined in the message encoding


publishedAt
Timetoken
A Pubnub TimeToken associated to the time when this message was published


originalMessage
dynamic
The orignal message encoded within the BaseMessage retreived from Pubnub



Intented Usage
The model is intented to be extended into various message types by the developer to differentiate between parsing strategies. Rime will provide JSON encoded RimeMessage's (or complete RimeMessage's when possible*) for you to parse and populate your UI with.
The provided TextMessage is a simple example of creating a typed extension to RimeMessage. It contains a text custom property to map to its payload, which is JSON encoded.
/// Message only containing text
class TextMessage extends RimeMessage {
///The type of the message when it is a rime message
static const String RIME_MESSAGE_TYPE = 'text-message';

/// Content
String text;

///Constructor to create a text message
TextMessage._({@required this.text, @required RimeMessage message})
: assert(message != null),
assert(message.type == RIME_MESSAGE_TYPE),
super(
uuid: message.uuid,
type: message.type,
content: message.content,
publishedAt: message.publishedAt,
originalMessage: message.originalMessage);

/// Parsing constructor,
/// Converts the RimeMessage object into a textmessage object by parsing its content
factory TextMessage.fromRimeMessage(RimeMessage message) {
//Extract text object from content
String text = message.content['text'];

return TextMessage._(text: text, message: message);
}

/// Creates a Rime Message payload from defined inputs
static Map<String, dynamic> toPayload(String text) {
return {'text': text};
}
}
copied to clipboard
In this example a simple switch statement can be employed to decode a RimeMessage into a TextMessage
// Fulled populated BaseMessage object retreived from Pubnub
RimeMessage encodeByType(BaseMessage receivedMessage){
// Decoded into a RimeMessage
RimeMessage rimeMessage = RimeMessage.fromBaseMessage(receivedMessage);

// Decodes RimeMessage absed on type
switch (rimeMessage.type) {

//Parse text message
case TextMessage.RIME_MESSAGE_TYPE:
return TextMessage.fromRimeMessage(rimeMessage);

// Other case statements associated to various types and parsers
...

default:
throw('No type decoder found')
}
}
copied to clipboard
RimeAPI #
The RimeAPI contains a set of requests to manipluate and format the Pubnub storage instance.
❗ The RimeRepository must be initialized before RimeAPI functions can be used.
Requests
The following is a specification of all RimeAPI requests.



Request name
Input(s)
Output
Description
Assertions




createChannel
List<String> users
RimeChannel
Creates a new RimeChannel between the inputted userIds. Creates a membership for each of the users and adds the channel to each user's next available Channel Groups.Outputs a fully populated RimeChannel
The authenticated user's userId must be within the list of userIds.


deleteChannel
String channelId
bool
Updates the user's channel membership settings to indicate that the user has deleted messages within a channel for themselves. This is represented as the Timetoken value of the last sent message.The value indicates to the UI that messages past the defined Timetoken should be ignored.Outputs the sucess of the request.
The authenticated user must be a member of the channel.


getChannel
String channelId
RimeChannel
Retreives a fully populated channel by the inputted channelId
The authenticated user must be a member of the channel.


getMostRecentChannels
(optional) int limit(optional) String start
Tuple2<List<RimeChannel>, String>
Paginated get channels reqeust for authenticated user. Retreived the list of channels ordered by lastUpdated.Channels are retreived relative to a start cursor. If no start==null then the start of the channels list is returned.The amount of channels requested is defined by the limit input, defaulted to 50.Along with the list of channels, the next cursor is outputted.
Always retrieves requests for authenticated user


leaveChannel
String userIdString channelId
void
Destroys a user's membership to a channel and removes the channel from its channel group.This request is not necissarily targetted at the authenticated user. it can be used to kick a user out of a channel aswell.
The input user but be a member within the channel


sendMessage
String channelIdMap<String, dynamic> message
Tuple2<ChannelMetaDetails, RimeMessage>
Sends the JSON payload in the message input to the channel defined by channelId.Returns sent message along with any channel update results from Pubnub
The user within the message payload must be a member of the channel.The message JSON must be an encoded RimeMessage



❗ Ensure that the assertions for each requests are met so that they may operate properly.
👍 All requests are async
👍 Example request to create channel:
RimeChannel newChannel = await RimeAPI.createChannel(['userId1', 'userId2']);
copied to clipboard
RimeState #
RimeState is the type of state that the RimeBloc holds. It is manipulated through RimeEvents sent the Bloc.
Refer to the bloc and flutter_bloc package documentations for insight on the relationships between States and Blocs with BlocBuilder and BlocListener.
RimeEmptyState
Innitial state for the RimeBloc does not store any values. while on this state, RimeBloc limits the RimeEvents that can be triggered to:

InitiailizeRime
ClearRime

RimeLiveState
The RimeLiveState is entered when the InitializeRime event is triggered in the RimeBloc. The state acts as a cache for channels, allowing you subscribe to ever updating RimeChannel information application wide.
👍 Along with maintiaing a channel cache, the state also maintiains the time ordering of channels.
👍 While on RimeLiveState you are able to send all RimeEvents to RimeBloc
Properties
The following properties are cahced within the most relavent RimeLiveState for the RimeBloc



Name
Type
Description




timeToken
int
The Timetoken value associated to the last update of the RimeLiveState


pageToken
String
The page index for the last GetChannels event


orgainizedChannels
List<String>
A time decending ordered list of channelIds stored within the current state


storedChannels
Map<String, RimeChannel>
The cache of channels mapped to channelId allowing of O(1) retreival of a RimeChannel by Id



These properties are aviable to the developer when subscribing to RimeBloc or accessing RimeBloc().state.
❗ RimeBloc().state statically retreives the state and will not record future state changes past the retreival.
Retrieve Channels
Retrieve a list of chronologically ordered channels based off the last message sent from PubNub.
RimeLiveState liveState = state as RimeLiveState;

List<RimeChannel> channels = liveState.orgainizedChannels.map<RimeChannel>((channel){
return liveState.storedChannels[channel];
}).toList();
copied to clipboard
RimeEvent #
Each RimeEvent maps to unique logic within the RimeBloc and are added to the Bloc to manipulate the RimeState. Events are resposible for safely calling RimeAPI and RimeRepository requests by ensuring input parameters and updating the state relative to the results. You will be resposible for invoking specific RimeEvents during the lifecycle of your application.
Refer to the bloc package documentation for insight on the relationships between Events and Blocs.
Initialize Event
Initiailizes the RimeBloc by initiailizing RimeRepository on the defined userId. The event subscribes RimeBloc to RimeRepository to manage manipulations to channels based on incoming messages. The RimeBloc then moves to RimeLiveState and invokes a GetChannels Event to retreive the innitial set of channels.
//Example
RimeBloc().add(InitializeRime('userId'));
copied to clipboard
👍 InitializeRime is the only way to move from RimeEmptyState to a fresh RimeLiveState
Get Channels Event
Calls the RimeAPI.getMostRecentChannels request passing the start index as the previously stored pageIndex within the current RimeLiveState. Adds and sorts new Channels to the RimeLiveState.
//Example
RimeBloc().add(GetChannelsEvent());
copied to clipboard
❗ Can only be called when RimeBloc().state is RimeLiveState
👍 GetChannels is called for the first time automatically in the InitializeRime event.
Create Channel Event
Calls the RimeAPI.createChannel request passing in the users directly to the request. Adds and sorts the new channel to the RimeLiveState. an onSuccess callback returns the newly created channel.

//Example
RimeBloc().add(CreateChannelEvent(users: ['user1', 'user2'], onSuccess: (channel){}));
copied to clipboard
❗ Can only be called when RimeBloc().state is RimeLiveState
Send Message Event
Calls the RimeAPI.sendMessage request by encoding the payload and type into a RimeMessage JSON encoding along with the authenticated user. The returned RimeMessage is used to update a cached RimeChannel within the current RimeLiveState with the message properties.
//Example
RimeBloc().add(MessageEvent('ChannelId', TextMessage.RIME_MESSAGE_TYPE, TextMessage.toPayload('Hello world')));
copied to clipboard
❗ Can only be called when RimeBloc().state is RimeLiveState
Delete Event
Calls the RimeAPI.deleteChannel to update the authenticated user's channel membership settings to reflect the message being deleted at the last message Timetoken. This updates a cached RimeChannel within the current RimeLiveState with the updated memeebrship properties.
//Example
RimeBloc().add(DeleteEvent('ChannelId'));
copied to clipboard
❗ Can only be called when RimeBloc().state is RimeLiveState
Leave Event
Calls the RimeAPI.leaveChannel to remeove a channel membership for the authenticated user. The channel is removed from the current RimeLiveState
Leave a channel corresponds to a userId and will delete the channel from the state. Will only delete for the corresponding userId which intialized Rime.
//Example
RimeBloc().add(LeaveEvent('ChannelId'));
copied to clipboard
❗ Can only be called when RimeBloc().state is RimeLiveState
Store Event
Takes a RimeChannel object and stores the into the current RimeLiveState. Upon storage the channels within the state are sorted.
//Example
RimeBloc().add(StoreEvent(exsistingChannel));
copied to clipboard
❗ Can only be called when RimeBloc().state is RimeLiveState
❗ Any exsisting channels mapped to the channelId of the new channel are overriden, so be careful with this event
❗ The RimeChannel must have a non-null lastUpdated token and channelId to be stored
Clear Event
Clears a RimeBloc by moving it to RimeEmptyState. This removes all caching within RimeLiveState.
//Example
RimeBloc().add(ClearRimeEvent());
copied to clipboard
Retreiving Channel History #
Rime exposes the ChannelStateProvider which is a state provider for listening to the channel history and retreiveing associated relatvent RimeChannel. The provider accomplishes this through a subscription to RimeRepository (for new messages) and RimeBloc (for channel updates).
The builder and listener function within the consturctor are used to provide the associated channel history and RimeChannel to any components using it.
//Example
ChannelStateProvider(
channelId: 'ChannelId',
controller: _controller, //ChannelProviderController
loadSize: 30 //Amount of messages per load
builder: (context, channel, messages){
//Build UI based on channel and messages
...
}
listener: (context, channel, messages){
//Repsond to state changes
...
}
)
copied to clipboard
👍 the ChannelStateProvider automatically converts the BaseMessages into RimeMessages
👍 when using ChannelStateProvider expect the most up to date information about a channel
❗ The channel must be a valid user channel
Loading more messages
The ChannelProviderController can be defined and binded to the ChannelStateProvider to controll the loading and resetting of state channel history.
Define a controller and bind it to ChannelStateProvider:
ChannelProviderController controller = ChannelProviderController();
ChannelStateProvider(
controller: _controller, //ChannelProviderController binded
...
)
copied to clipboard
Loading more messages can be done with a binded controller. The request is async and returns bool representing if there is more messages in the channnel history:
//Example on binded controller
bool hasMore = await controller.loadMore();
copied to clipboard
Resetting the messages within the history state to the innitial load can be done with a binded controller. The request is async and returns bool representing if there is more messages in the channnel history:
//Example on binded controller
bool hasMore = await controller.reset();
copied to clipboard
👍 When you're done with the controller be sure to dispose it to unbind the dirty ChannelStateProviderState:
//Example on binded controller
controller.dispose();
copied to clipboard

License:

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Files In This Product:

Customer Reviews

There are no reviews.