Fundamentals

Shared states

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


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 'discordjs'

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 a shared state

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

dart run mineral make:state <MyState>

The file is structured in 2 main parts :

  • The state: represents the shared data
  • The actions: they allow you to make mutations to your shared state
import 'package:mineral/framework.dart';

class MyCounterState extends MineralState<int> {
      MyCounterState(): super(0);
      
      /// Increment state
      void increment () => state++;
      
      /// Decrement state
      void decrement () => state--;
      
      // reset state to 0
      void reset () => state = 0;
      
      /// and more methos...
}

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

import 'package:mineral/core.dart';
import './my_counter_state.dart';

void main () async {
    Kernel kernel = Kernel(
      intents: IntentService(all: true),
      states: SharedStateService([MyCounterState()]),
    );
		
     await kernel.init();
}

Consume our state

It is very easy to consume our state through your application. Indeed, once registered within your main.dart or your module entry point, the class is automatically integrated into the Mineral framework dependency container.

Your state is accessible through two means:

  • Component classes such as events or commands to name a few
  • The Mineral framework dependency container

The classes component

All of your registered stores within your application are accessible from all of your component classes, we'll look at an example of using an event class.

In order to access the states of your application, we need to access the context of our application using the MineralContext mixin.

import 'package:mineral/framework.dart';
import 'package:mineral/core/events.dart';
import '../my_counter_state.dart';
import 'package:mineral/core/extras.dart'; 👈

class MessageCreate extends MineralEvent<MessageCreateEvent> with State 👈 {
  Future<void> handle (MessageCreateEvent event) async {
        final counter = states.use<MyCounterState>();
        print(counter.state); // 0
        
        counter.increment();
        print(counter.state); // 1
    
        counter.decrement();
        print(counter.state); // 0
    
        counter.increment();
        print(counter.state); // 1
    
        counter.reset(); 
        print(counter.state); // 0
  }
}

The dependency container

In the event that you need to access your state from another file in your application that does not allow you to do so natively, you can use the dependency container directly to retrieve your stores.

void main () {
      final StoreManager manager = ioc.use<StateManager>();
      final counter = states.use<MyCounterState>();
    
      counter.increment();
      print(counter.state); // ++
      counter.decrement(); 
      print(counter.state); // --
      counter.reset();
      print(counter.state); // 0
}

And now you can use your shared state throughout your application ! We will see in next time how to use shared states to do an configuration (yml) for your bot.

Previous
Context menus