Global States

It is sometimes difficult to share data through your applications, the Mineral framework offers an essential component to overcome this problem.


Introduction

Under the Node runtime with the Javascript language, you are advised when you start developing discord applications to develop your entire application within a single file, your index.js.

import { Client, GatewayIntentBits } from 'discord.js'

const foo = 'bar'
const client = Client({ intents: [GatewayIntentBits.Guilds] })

client.once('ready', () => {
  console.log(`${foo} is ready !`)
});

client.on('messageCreate', () => {
  console.log(`${foo} is ready !`)
});

client.login('...')

So each of your listeners can access every variable defined within it, but this practice is bad because your application will lose a lot of scalability and maintainability. This benefit is lost when we decide to decompose our application following a business logic.

In order to overcome this lack of accessibility, the Mineral framework allows you to design shared data throughout your application through a very important notion: shared states.

These shared states are represented as classes instantiated within your main.dart file or modules and will be directly injected within the instance of your events, commands or context menus.


Create global state

First, we will execute a command from the CLI of the Mineral framework.

mineral make:state <name>

This command will create a new file in the target directory of your project. This file will contain a class that will be used to store your shared data.

final class CounterState implements GlobalState<int> {
  @override
  int state = 0;

  @override
  void increment() => state++;

  @override
  void decrement() => state--;
}
Note

Our T state is seen as a getter and not a property, this prevents redefining the value of the variable from outside the class.

Once your blind is created, we will add it to the main.dart file or to your module.

void main(_, port) async {
  final client = Client()
    .setHmrDevPort(port)
    .build();

  client.register(CounterState.new);
}

Usage with abstract contract

final class CounterState implements GlobalState<int> { 
final class CounterState implements CounterStateContract { 
  @override
  int state = 0;

  @override
  void increment() => state++;

  @override
  void decrement() => state--;
}

Once your contract is implemented, we can override the state binding with generic.

void main(_, port) async {
  final client = Client()
    .setHmrDevPort(port)
    .build();

  client.register(CounterState.new);
  client.register<CounterStateContract>(CounterState.new);
}

Retrieve global state

In the followed section, we consider that the state is a counter with CounterStateContract named binding.

To retrieve a global state, we provide an State mixin that allows you to inject states into your class for easy access.

final class MyEvent extends MessageCreateEvent { 
final class MyEvent extends MessageCreateEvent with State { 
  @override
  Future<void> handle(ServerMessage message) async {
    final counter = state.read<CounterStateContract>();
    counter.increment();

    print('Counter value: ${counter.state}');
  }
}