Skip to content

Velen Pagination Helper

Mihou edited this page Jul 14, 2021 · 5 revisions

📃 Velen Pagination

Velen also offers a helper class that helps you paginate items easily with the Paginate<T> class. All you need is to add a single line, as seen below:

new Paginate<?>(someList).paginate(MessageCreateEvent, PaginateEvent<?>(){...},
Duration);

An example implementation of the above is:

VelenCommand.of("paginate", "Tests pagination.", velen, (event, message, user, args) -> {

  List<String> testList = Arrays.asList("Test 0", "Test 1", "Test 2", "Test 3", "Test 4");
  
  // Create a Pagination object (you can also save the pagination object to reuse later).
  new Paginate<>(testList).paginate(event, new PaginateEvent<String>() {

    private EmbedBuilder embed(String currentItem, int arrow, int maximum) {
    
      // Remember to always add +1 to arrow for these types of stuff since
      // arrow returns the raw position which means it starts at 0 instead of 1.
      return new EmbedBuilder().setTitle("Item [" + (arrow + 1) + "/" + maximum + "]")
        .setDescription(currentItem).setColor(Color.BLUE);
        
    }

    private EmbedBuilder embed(String currentItem) {
      return new EmbedBuilder().setTitle("Item")
        .setDescription(currentItem).setColor(Color.BLUE);
    }

    @Override
    public MessageBuilder onInit(MessageCreateEvent event, String currentItem,
      int arrow, Paginator<String> paginator) {
      
      // This is the initial message that will be sent on start of pagination.
      return new MessageBuilder().setEmbed(embed(currentItem, arrow, paginator.size()));
      
    }

    @Override
    public void onPaginate(MessageCreateEvent event, Message paginateMessage, String currentItem,
      int arrow, Paginator<String> paginator) {
      
      // This is what will be executed when you paginate next or backwards.
      paginateMessage.edit(embed(currentItem, arrow, paginator.size()));
      
    }

    @Override
    public MessageBuilder onEmptyPaginator(MessageCreateEvent event) {
    
      // This is sent when the paginator has no items.
      return new MessageBuilder().setContent("There are currently no items!");
      
    }

    @Override
    public void onSelect(MessageCreateEvent event, Message paginateMessage, String itemSelected,
      int arrow, Paginator<String> paginator) {
      
      // Similar to onPaginate except this is sent whenever the user
      // has selected a page that they want.
      paginateMessage.edit(embed(itemSelected));
      
    }
    
  }, Duration.ofMinutes(5));
}).attach();

Similar to VelenEvent, you can also place the handler onto its own class. An example of such can be seen below:

ExamplePaginateEvent.class

This is the handler class for pagination.

class ExamplePaginateEvent implements PaginateEvent<String> {

    private EmbedBuilder embed(String currentItem, int arrow, int maximum) {
      return new EmbedBuilder().setTitle("Item [" + (arrow + 1) + "/" + maximum + "]")
        .setDescription(currentItem).setColor(Color.BLUE);
    }

    private EmbedBuilder embed(String currentItem) {
      return new EmbedBuilder().setTitle("Item")
        .setDescription(currentItem).setColor(Color.BLUE);
    }

    @Override
    public MessageBuilder onInit(MessageCreateEvent event, String currentItem,
      int arrow, Paginator<String> paginator) {
      return new MessageBuilder().setEmbed(embed(currentItem, arrow, paginator.size()));
    }

    @Override
    public void onPaginate(MessageCreateEvent event, Message paginateMessage, String currentItem,
      int arrow, Paginator<String> paginator) {
      paginateMessage.edit(embed(currentItem, arrow, paginator.size()));
    }

    @Override
    public MessageBuilder onEmptyPaginator(MessageCreateEvent event) {
      return new MessageBuilder().setContent("There are currently no items!");
    }

    @Override
    public void onSelect(MessageCreateEvent event, Message paginateMessage, String itemSelected,
      int arrow, Paginator<String> paginator) {
      paginateMessage.edit(embed(itemSelected));
    }
    
}

Main.class

The main class where you register all your Velen commands.

VelenCommand.of("paginate", "Tests pagination.", velen, (event, message, user, args) -> {
  List<String> testList = Arrays.asList("Test 0", "Test 1", "Test 2", "Test 3", "Test 4");
  new Paginate<>(testList).paginate(event, new ExamplePaginateEvent(), Duration.ofMinutes(5));
}).attach();

Optionally, you can customize the emojis (unicode or Javacord Emoji objects) that will be used, for example (this example will use Unicode):

new Paginate<>(items, "➡", "⬅", "👍", "👎").paginate(...);

Paginate with Buttons

With v1.1.0, Velen now officially supports pagination with buttons. There are two types of pagination on Velen as usual which is called Normal and Simple with the Simple lacking a Select reaction (or button), an example implementation of both types for buttons is shown below.

To explain a little bit more simply, to create a Paginate event on Velen, all you need is to add this line:

new Paginate<?>(someList).paginateWithButtons(Unique Identifier, MessageCreateEvent, PaginateButtonEvent<?> () {...},
Duration);

You can set the Duration to Duration.ofSeconds(0) to stop Velen from automatically removing the button listeners when the duration time has passed (this is to free up unused listeners).

Paginate Normal Paginate Simple

Paginate Normal (Button)

VelenCommand.of("paginate", "Paginates through a list of string.", velen, (event, message, user, args) -> {
          List<String> list = Arrays.asList("sparkling beauty.", "dangerous assassin.", "cute but deadly.", "sparkling star.");

          new Paginate<String>(list).paginateWithButtons("someKindOfUniqueThingHere", event, new PaginateButtonEvent<String> () {

            @Override
            public MessageBuilder onInit(MessageCreateEvent event, String currentItem, int arrow, Paginator<String> paginator) {
              return new MessageBuilder().setEmbed(paginateEmbed(currentItem, arrow, paginator.size()));
            }

            @Override
            public void onPaginate(InteractionImmediateResponseBuilder responder,
              MessageCreateEvent event, Message paginateMessage,
              String currentItem, int arrow, Paginator<String> paginator) {

              paginateMessage.edit(paginateEmbed(currentItem, arrow, paginator.size()));

              // This is a required line (especially if you are editing the message not through
              // the responder).
              responder.respond();

            }

            @Override
            public MessageBuilder onEmptyPaginator(MessageCreateEvent event) {

              return new MessageBuilder()
                .setContent("**ERR**:: There are no items found.");

            }

            @Override
            public void onSelect(InteractionImmediateResponseBuilder responder,
              MessageCreateEvent event, Message paginateMessage,
              String itemSelected, int arrow, Paginator<String> paginator) {

              paginateMessage.delete();
              responder.setContent("You are now a " + itemSelected).respond();

            }

            private EmbedBuilder paginateEmbed(String currentItem, int arrow, int maximum) {
              return new EmbedBuilder()
                .setColor(Color.GREEN)
                .setTitle("Class [" + (arrow + 1) + "/" + maximum + "]")
                .setDescription("You are selecting the class: " + currentItem);
            }

          }, Duration.ofMinutes(5));
}).attach();

Paginate Simple (Button)

VelenCommand.of("paginate", "Paginates through a list of string.", velen, (event, message, user, args) -> {
            List<String> list = Arrays.asList("sparkling beauty.", "dangerous assassin.", "cute but deadly.", "sparkling star.");

            // The emojis (unicode) on the params is simply setting the
            // cancel reaction to a trash can.
            new Paginate<String>(list, "➡", "⬅", "👍", "\uD83D\uDDD1").paginateWithButtons("someKindOfUniqueThingHere", event, new PaginateButtonSimpleEvent<String>() {

                @Override
                public MessageBuilder onInit(MessageCreateEvent event, String currentItem, int arrow, Paginator<String> paginator) {
                    return new MessageBuilder().setEmbed(paginateEmbed(currentItem, arrow, paginator.size()));
                }

                @Override
                public void onPaginate(InteractionImmediateResponseBuilder responder,
                                                                      MessageCreateEvent event, Message paginateMessage,
                                                                      String currentItem, int arrow, Paginator<String> paginator) {

                    paginateMessage.edit(paginateEmbed(currentItem, arrow, paginator.size()));

                    // This is a required line (especially if you are editing the message not through
                    // the responder).
                    responder.respond();

                }

                @Override
                public MessageBuilder onEmptyPaginator(MessageCreateEvent event) {
                    return new MessageBuilder()
                            .setContent("**ERR**:: There are no items found.");
                }

                private EmbedBuilder paginateEmbed(String currentItem, int arrow, int maximum) {
                    return new EmbedBuilder()
                            .setColor(Color.GREEN)
                            .setTitle("Class [" + (arrow + 1) + "/" + maximum +"]")
                            .setDescription("You are looking at the class: " + currentItem);
                }

            }, Duration.ofMinutes(5));
}).attach();

Common Issues

Q: Paginating with buttons somehow gives This interaction failed even though the message was changed, etc...

A: This is because you are not adding the line: responder.respond() or something similar which is needed to tell Discord that you responded to the event (Velen, by default, does not add it since we don't want to reduce your rate-limit by sending a duplicate empty response).

Q: The buttons aren't removed when Duration of removeAfter has passed...

A: This is a known-issue as Javacord (or maybe I haven't found it) has no way of removing components from the message for some reason.