Skip to content

Inline keyboard pagination #438

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

Closed
Closed
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

## [0.41.0] - 2017-03-25
### Added
- `$showInHelp` attribute for commands, to set if it should be displayed in the `/help` command.
- `$show_in_help` attribute for commands, to set if it should be displayed in the `/help` command.
- Link to new Telegram group: `https://telegram.me/PHP_Telegram_Bot_Support`
- Introduce change log.

Expand Down
96 changes: 96 additions & 0 deletions src/Entities/InlineKeyboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,100 @@
*/
class InlineKeyboard extends Keyboard
{
/**
* Get an inline pagination keyboard.
*
* - $callback_data is an ID for the CallbackqueryCommand, to know where the request comes from.
* The ID is automatically appended with '_page_%d' to filter out the selected page number.
*
* - $labels allows for custom button labels, using '%d' placeholders.
* Default:
* ```
* [
* 'first' => '« %d',
* 'previous' => '‹ %d',
* 'current' => '· %d ·',
* 'next' => '%d ›',
* 'last' => '%d »',
* ]
* ``
*`
* initial idea from: https://stackoverflow.com/a/42879866
*
* @param string $callback_data
* @param int $current_page
* @param int $max_pages
* @param array $labels
*
* @return \Longman\TelegramBot\Entities\InlineKeyboard
*/
public static function getPagination($callback_data, $current_page, $max_pages, array $labels = [])
{
$callback_data .= '_page_%d';

// Merge labels with defaults.
$labels = array_merge([
'first' => '« %d',
'previous' => '‹ %d',
'current' => '· %d ·',
'next' => '%d ›',
'last' => '%d »',
], $labels);
$pages = [
'first' => 1,
'previous' => $current_page - 1,
'current' => $current_page,
'next' => $current_page + 1,
'last' => $max_pages,
];

// Set labels for keyboard, replacing placeholders with page numbers.
foreach ($labels as $key => &$label) {
if (strpos($label, '%d') !== false) {
$label = sprintf($label, $pages[$key]);
} elseif ($label === '') {
$label = null;
}
}
unset($label);

$callbacks_data = [];
foreach ($pages as $key => $page) {
$callbacks_data[$key] = sprintf($callback_data, $page);
}

$buttons = [];

if ($current_page > 1 && $labels['first'] !== null) {
$buttons[] = new InlineKeyboardButton(['text' => $labels['first'], 'callback_data' => $callbacks_data['first']]);
}
if ($current_page > 2 && $labels['previous'] !== null) {
$buttons[] = new InlineKeyboardButton(['text' => $labels['previous'], 'callback_data' => $callbacks_data['previous']]);
}

if ($labels['current'] !== null) {
$buttons[] = new InlineKeyboardButton(['text' => $labels['current'], 'callback_data' => $callbacks_data['current']]);
}

if ($current_page < $max_pages - 1 && $labels['next'] !== null) {
$buttons[] = new InlineKeyboardButton(['text' => $labels['next'], 'callback_data' => $callbacks_data['next']]);
}
if ($current_page < $max_pages && $labels['last'] !== null) {
$buttons[] = new InlineKeyboardButton(['text' => $labels['last'], 'callback_data' => $callbacks_data['last']]);
}

return new InlineKeyboard($buttons);
}

/**
* Extract the page number from the passed callback data.
*
* @param string $callback_data
*
* @return int
*/
public static function getPageFromCallbackData($callback_data)
{
return (int) preg_replace('/.*_page_(\d+)$/', '$1', $callback_data);
}
}
39 changes: 39 additions & 0 deletions src/Entities/InlineKeyboardPaginator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/**
* This file is part of the TelegramBot package.
*
* (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Longman\TelegramBot\Entities;

interface InlineKeyboardPaginator
{
/**
* A unique identifier for the callback query.
*
* @return string
*/
public static function getCallbackDataId();

/**
* Get the output for the currently selected page.
*
* @param int $current_page
*
* @return string
*/
public static function getOutput($current_page);

/**
* Get the pagination for the current page.
*
* @param int $current_page
*
* @return InlineKeyboard
*/
public static function getPagination($current_page);
}
2 changes: 1 addition & 1 deletion src/Telegram.php
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ public function handle()
*
* @return string
*/
private function getCommandFromType($type)
protected function getCommandFromType($type)
{
return $this->ucfirstUnicode(str_replace('_', '', $type));
}
Expand Down
155 changes: 118 additions & 37 deletions tests/unit/Entities/InlineKeyboardTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,51 +54,60 @@ public function testInlineKeyboardDataMalformedSubfield()

public function testInlineKeyboardSingleButtonSingleRow()
{
$inline_keyboard = (new InlineKeyboard(
$keyboard = new InlineKeyboard(
$this->getRandomButton('Button Text 1')
))->getProperty('inline_keyboard');
self::assertSame('Button Text 1', $inline_keyboard[0][0]->getText());
);
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1'],
], 'text', $keyboard);

$inline_keyboard = (new InlineKeyboard(
$keyboard = new InlineKeyboard(
[$this->getRandomButton('Button Text 2')]
))->getProperty('inline_keyboard');
self::assertSame('Button Text 2', $inline_keyboard[0][0]->getText());
);
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 2'],
], 'text', $keyboard);
}

public function testInlineKeyboardSingleButtonMultipleRows()
{
$keyboard = (new InlineKeyboard(
$keyboard = new InlineKeyboard(
$this->getRandomButton('Button Text 1'),
$this->getRandomButton('Button Text 2'),
$this->getRandomButton('Button Text 3')
))->getProperty('inline_keyboard');
self::assertSame('Button Text 1', $keyboard[0][0]->getText());
self::assertSame('Button Text 2', $keyboard[1][0]->getText());
self::assertSame('Button Text 3', $keyboard[2][0]->getText());
);
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1'],
['Button Text 2'],
['Button Text 3'],
], 'text', $keyboard);

$keyboard = (new InlineKeyboard(
$keyboard = new InlineKeyboard(
[$this->getRandomButton('Button Text 4')],
[$this->getRandomButton('Button Text 5')],
[$this->getRandomButton('Button Text 6')]
))->getProperty('inline_keyboard');
self::assertSame('Button Text 4', $keyboard[0][0]->getText());
self::assertSame('Button Text 5', $keyboard[1][0]->getText());
self::assertSame('Button Text 6', $keyboard[2][0]->getText());
);
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 4'],
['Button Text 5'],
['Button Text 6'],
], 'text', $keyboard);
}

public function testInlineKeyboardMultipleButtonsSingleRow()
{
$keyboard = (new InlineKeyboard([
$keyboard = new InlineKeyboard([
$this->getRandomButton('Button Text 1'),
$this->getRandomButton('Button Text 2'),
]))->getProperty('inline_keyboard');
self::assertSame('Button Text 1', $keyboard[0][0]->getText());
self::assertSame('Button Text 2', $keyboard[0][1]->getText());
]);
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1', 'Button Text 2'],
], 'text', $keyboard);
}

public function testInlineKeyboardMultipleButtonsMultipleRows()
{
$keyboard = (new InlineKeyboard(
$keyboard = new InlineKeyboard(
[
$this->getRandomButton('Button Text 1'),
$this->getRandomButton('Button Text 2'),
Expand All @@ -107,32 +116,104 @@ public function testInlineKeyboardMultipleButtonsMultipleRows()
$this->getRandomButton('Button Text 3'),
$this->getRandomButton('Button Text 4'),
]
))->getProperty('inline_keyboard');
);

self::assertSame('Button Text 1', $keyboard[0][0]->getText());
self::assertSame('Button Text 2', $keyboard[0][1]->getText());
self::assertSame('Button Text 3', $keyboard[1][0]->getText());
self::assertSame('Button Text 4', $keyboard[1][1]->getText());
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1', 'Button Text 2'],
['Button Text 3', 'Button Text 4'],
], 'text', $keyboard);
}

public function testInlineKeyboardAddRows()
{
$keyboard_obj = new InlineKeyboard([]);
$keyboard = new InlineKeyboard([]);

$keyboard_obj->addRow($this->getRandomButton('Button Text 1'));
$keyboard = $keyboard_obj->getProperty('inline_keyboard');
self::assertSame('Button Text 1', $keyboard[0][0]->getText());
$keyboard->addRow($this->getRandomButton('Button Text 1'));
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1'],
], 'text', $keyboard);

$keyboard_obj->addRow(
$keyboard->addRow(
$this->getRandomButton('Button Text 2'),
$this->getRandomButton('Button Text 3')
);
$keyboard = $keyboard_obj->getProperty('inline_keyboard');
self::assertSame('Button Text 2', $keyboard[1][0]->getText());
self::assertSame('Button Text 3', $keyboard[1][1]->getText());
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1'],
['Button Text 2', 'Button Text 3'],
], 'text', $keyboard);

$keyboard->addRow($this->getRandomButton('Button Text 4'));
KeyboardTest::assertAllButtonPropertiesEqual([
['Button Text 1'],
['Button Text 2', 'Button Text 3'],
['Button Text 4'],
], 'text', $keyboard);
}

$keyboard_obj->addRow($this->getRandomButton('Button Text 4'));
$keyboard = $keyboard_obj->getProperty('inline_keyboard');
self::assertSame('Button Text 4', $keyboard[2][0]->getText());
public function testInlineKeyboardPagination()
{
// Should get '_page_%d' appended to it.
$callback_data = 'cbdata';

// current
$keyboard = InlineKeyboard::getPagination($callback_data, 1, 1);
KeyboardTest::assertAllButtonPropertiesEqual([
['· 1 ·'],
], 'text', $keyboard);
KeyboardTest::assertAllButtonPropertiesEqual([
['cbdata_page_1'],
], 'callback_data', $keyboard);

// current, next, last
$keyboard = InlineKeyboard::getPagination($callback_data, 1, 10);
KeyboardTest::assertAllButtonPropertiesEqual([
['· 1 ·', '2 ›', '10 »'],
], 'text', $keyboard);
KeyboardTest::assertAllButtonPropertiesEqual([
['cbdata_page_1', 'cbdata_page_2', 'cbdata_page_10'],
], 'callback_data', $keyboard);

// first, previous, current, next, last
$keyboard = InlineKeyboard::getPagination($callback_data, 5, 10);
KeyboardTest::assertAllButtonPropertiesEqual([
['« 1', '‹ 4', '· 5 ·', '6 ›', '10 »'],
], 'text', $keyboard);
KeyboardTest::assertAllButtonPropertiesEqual([
['cbdata_page_1', 'cbdata_page_4', 'cbdata_page_5', 'cbdata_page_6', 'cbdata_page_10'],
], 'callback_data', $keyboard);

// first, previous, current, last
$keyboard = InlineKeyboard::getPagination($callback_data, 9, 10);
KeyboardTest::assertAllButtonPropertiesEqual([
['« 1', '‹ 8', '· 9 ·', '10 »'],
], 'text', $keyboard);
KeyboardTest::assertAllButtonPropertiesEqual([
['cbdata_page_1', 'cbdata_page_8', 'cbdata_page_9', 'cbdata_page_10'],
], 'callback_data', $keyboard);

// first, previous, current
$keyboard = InlineKeyboard::getPagination($callback_data, 10, 10);
KeyboardTest::assertAllButtonPropertiesEqual([
['« 1', '‹ 9', '· 10 ·'],
], 'text', $keyboard);
KeyboardTest::assertAllButtonPropertiesEqual([
['cbdata_page_1', 'cbdata_page_9', 'cbdata_page_10'],
], 'callback_data', $keyboard);

// custom labels, skipping some buttons
// first, previous, current, next, last
$keyboard = InlineKeyboard::getPagination($callback_data, 5, 10, [
'first' => '',
'previous' => 'previous %d',
'current' => null,
'next' => '%d next',
'last' => '%d last',
]);
KeyboardTest::assertAllButtonPropertiesEqual([
['previous 4', '6 next', '10 last'],
], 'text', $keyboard);
KeyboardTest::assertAllButtonPropertiesEqual([
['cbdata_page_4', 'cbdata_page_6', 'cbdata_page_10'],
], 'callback_data', $keyboard);
}
}
Loading