blackcat.js-discord - v1.0.10
    Preparing search index...

    Class PaginationBuilder<T>

    Type Parameters

    • T
    Index

    Constructors

    • Khởi tạo một instance PaginationBuilder

      Type Parameters

      • T

      Parameters

      Returns PaginationBuilder<T>

      Constructor này chịu trách nhiệm:

      • Lưu lại cấu hình (options)
      • Xác định nguồn dữ liệu:
        • Nếu có pages → dùng dữ liệu tĩnh
        • Nếu không → dùng pageLoader + totalPages
      • Thiết lập totalPages ban đầu

      Có 2 cách sử dụng chính:

      Khi bạn đã có sẵn toàn bộ dữ liệu:

      • pages sẽ được ưu tiên
      • totalPages được tính tự động từ pages.length

      Khi dữ liệu đến từ API / database:

      • Phải cung cấp totalPages
      • pageLoader sẽ được gọi khi cần load trang

      ⚠️ Nếu không cung cấp pages và cũng không có pageLoader thì hệ thống sẽ không có nguồn dữ liệu và sẽ throw error khi chạy.

      // Dùng dữ liệu tĩnh
      const pagination = new PaginationBuilder<string>({
      pages: ["Trang 1", "Trang 2", "Trang 3"],
      renderPage: ({ data }) => ({ content: data })
      });
      // Dùng dữ liệu động (API)
      const pagination = new PaginationBuilder<string[]>({
      totalPages: 10,
      pageLoader: async (page) => {
      const res = await fetch(`/api?page=${page + 1}`);
      const json = await res.json();
      return json.data;
      },
      renderPage: ({ data }) => ({
      content: data.join("\n")
      })
      });
      // Dùng database (Prisma)
      const pagination = new PaginationBuilder<User[]>({
      totalPages: 20,
      pageLoader: async (page) => {
      return prisma.user.findMany({
      skip: page * 10,
      take: 10
      });
      },
      renderPage: ({ data }) => ({
      content: data.map(u => u.username).join("\n")
      })
      });

    Properties

    state: Record<string, any> = {}

    Methods

    • Xóa toàn bộ cache của pagination

      Returns void

      void

      Method này sẽ xóa toàn bộ dữ liệu đã cache trong pageCache.

      Cache được sử dụng khi:

      • Dùng pageLoader
      • Tránh gọi lại API nhiều lần cho cùng một page

      Dùng khi:

      • Dữ liệu backend thay đổi
      • Muốn force reload data mới
      • Debug hoặc reset state

      ⚠️ Sau khi gọi:

      • Các lần getPageData tiếp theo sẽ gọi lại pageLoader
      // Force reload data từ API
      pagination.clearCache();
      await pagination.reload();
      // Sau khi update database
      await updateUser();
      pagination.clearCache();

      PaginationBuilder.getPageData

    • Chuyển đến trang đầu tiên

      Parameters

      • Optionalinteraction: StringSelectMenuInteraction<CacheType> | ButtonInteraction<CacheType>

        Interaction (button hoặc select menu) nếu có

      Returns Promise<void>

      Promise

      Đây là method helper, thực chất gọi: goto(0)

      Dùng khi:

      • Xử lý nút "First"
      • Reset pagination về trang đầu
      await pagination.first();
      
      // Reset về trang đầu sau khi filter
      await pagination.first();

      PaginationBuilder.goto

    • Lấy context hiện tại của pagination

      Returns Promise<PaginationRenderContext<T>>

      Promise<PaginationRenderContext>

      Method này trả về object context giống như khi gọi renderPage.

      Bao gồm:

      • page: trang hiện tại
      • totalPages: tổng số trang
      • data: dữ liệu trang hiện tại
      • pagination: instance hiện tại

      Dùng khi:

      • Cần lấy data + metadata hiện tại
      • Viết logic custom ngoài pagination
      • Debug trạng thái runtime
      const ctx = await pagination.getContext();
      console.log(ctx.page, ctx.data);
      // Dùng trong custom logic
      const { data } = await pagination.getContext();
      console.log("Current data:", data);

      Có thể throw nếu:

      • getPageData thất bại

      PaginationRenderContext

    • Lấy message hiện tại của pagination

      Returns Message<boolean>

      Message Message Discord đã được gửi

      Đây là message được tạo ra khi gọi start().

      ⚠️ Method này chỉ hợp lệ sau khi đã gọi start(). Nếu gọi trước đó có thể trả về undefined hoặc gây lỗi.

      Dùng khi:

      • Cần thao tác trực tiếp với message
      • Edit message ngoài pagination
      • Lấy message ID
      const msg = pagination.getMessage();
      console.log(msg.id);
      // Edit message thủ công
      const msg = pagination.getMessage();
      await msg.edit({ content: "Updated!" });
      // React vào message
      const msg = pagination.getMessage();
      await msg.react("👍");

      Có thể xảy ra lỗi nếu message chưa được khởi tạo (chưa gọi start)

      PaginationBuilder.start

    • Lấy số trang hiện tại

      Returns number

      number Trang hiện tại (0-based)

      Method này trả về index của trang hiện tại trong pagination.

      ⚠️ Giá trị trả về là 0-based:

      • Trang đầu = 0
      • Trang thứ 2 = 1

      Dùng khi:

      • Hiển thị thông tin trang
      • Logic điều kiện theo trang
      • Debug trạng thái pagination
      const page = pagination.getPage();
      console.log(`Trang hiện tại: ${page + 1}`);
      // Kiểm tra có phải trang đầu không
      if (pagination.getPage() === 0) {
      console.log("Đang ở trang đầu");
      }

      PaginationBuilder.getTotalPages

    • Lấy dữ liệu của một trang cụ thể

      Parameters

      • page: number

        Số trang (0-based index)

      Returns Promise<T>

      Promise Dữ liệu tương ứng với trang

      Đây là method core chịu trách nhiệm truy xuất dữ liệu cho pagination.

      Method này hỗ trợ 2 nguồn dữ liệu:

      • pages: dữ liệu tĩnh (array có sẵn)
      • pageLoader: dữ liệu động (load theo từng page)

      Đồng thời tích hợp cơ chế cache (nếu useCache = true) để tối ưu hiệu năng. Luồng xử lý:

      • Kiểm tra phạm vi page
      • Trả về trực tiếp pages[page]
      • Nếu useCache = true và đã có cache → trả về cache (cache hit)
      • Nếu chưa có → gọi pageLoader(page) (cache miss)
      • Sau khi load xong:
        • Lưu vào cache (nếu bật cache)
        • Trả về dữ liệu
      • Throw error

      ⚠️ Cache sử dụng Collection<number, T> với key là index của page.

      // Sử dụng dữ liệu tĩnh
      const pagination = new PaginationBuilder<string>({
      pages: ["A", "B", "C"],
      renderPage: ({ data }) => ({ content: data })
      });

      await pagination.getPageData(1); // "B"
      // Sử dụng dữ liệu động + cache
      const pagination = new PaginationBuilder<string[]>({
      totalPages: 5,
      useCache: true,
      pageLoader: async (page) => {
      console.log("Loading page:", page);
      return [`Item ${page}`];
      },
      renderPage: ({ data }) => ({
      content: data.join("\n")
      })
      });

      await pagination.getPageData(0); // gọi pageLoader
      await pagination.getPageData(0); // dùng cache
      • "Trang nằm ngoài phạm vi cho phép." nếu page không hợp lệ khi dùng pages
      • "Không có nguồn dữ liệu." nếu không có cả pagespageLoader
    • Lấy tổng số trang

      Returns number

      number Tổng số trang

      Giá trị này được xác định từ:

      • pages.length (nếu dùng dữ liệu tĩnh)
      • options.totalPages (nếu dùng pageLoader)

      Dùng khi:

      • Hiển thị UI (Trang X/Y)
      • Kiểm tra giới hạn pagination
      • Logic điều hướng
      const total = pagination.getTotalPages();
      console.log(`Tổng số trang: ${total}`);
      // Kiểm tra trang cuối
      if (pagination.getPage() === pagination.getTotalPages() - 1) {
      console.log("Đang ở trang cuối");
      }

      PaginationBuilder.getPage

    • Chuyển đến một trang cụ thể và cập nhật message

      Parameters

      • page: number

        Số trang cần chuyển đến (0-based)

      • Optionalinteraction: StringSelectMenuInteraction<CacheType> | ButtonInteraction<CacheType>

        Interaction (button hoặc select menu) nếu có

      Returns Promise<void>

      Promise

      Đây là method trung tâm (core) của hệ thống pagination.

      Hầu hết các hành động điều hướng như:

      • next()
      • prev()
      • first()
      • last()
      • select menu

      đều gọi vào method này.

      Quy trình hoạt động:

      • Nếu page < 0 → set về 0
      • Nếu page >= totalPages → set về totalPages - 1
      • Lưu lại oldPage
      • Gán this.page = page
      • Gọi getPageData(page)
      • Gọi renderPage
      • Build lại components
      • Nếu có interaction → dùng interaction.update()
      • Nếu không → dùng message.edit()
      • Gọi onPageChange (nếu có)

      ⚠️ Method này không tạo collector mới, chỉ update UI.

      // Chuyển đến trang 2
      await pagination.goto(2);
      // Dùng trong custom button
      onComponent: async ({ interaction, pagination }) => {
      if (interaction.customId === "go:page5") {
      await pagination.goto(4, interaction);
      }
      }
      // Gọi từ nội bộ (next/prev)
      await pagination.next(); // thực chất gọi goto(this.page + 1)
      // Boundary handling
      await pagination.goto(-10); // => page = 0
      await pagination.goto(999); // => page = last

      Có thể throw nếu:

      • getPageData thất bại (API lỗi, out of bounds,...)
      • renderPage lỗi
      • Discord API update message thất bại
      • PaginationBuilder.next
      • PaginationBuilder.prev
      • PaginationBuilder.first
      • PaginationBuilder.last
      • PaginationOptions.onPageChange
    • Kiểm tra trạng thái hoạt động của pagination

      Returns boolean

      boolean true nếu pagination đang chạy, ngược lại false

      Method này dùng để xác định xem InteractionCollector của pagination còn hoạt động hay không.

      Điều kiện để trả về true:

      • collector đã được khởi tạo
      • collector chưa bị kết thúc (ended = false)

      Dùng khi:

      • Cần kiểm tra trước khi thực hiện hành động (update, stop,...)
      • Tránh gọi method khi pagination đã dừng
      • Debug trạng thái runtime

      ⚠️ Method này không đảm bảo message còn tồn tại, chỉ kiểm tra trạng thái collector.

      if (pagination.isRunning()) {
      console.log("Pagination đang hoạt động");
      }
      // Tránh stop nhiều lần
      if (pagination.isRunning()) {
      await pagination.stop();
      }
      // Debug
      console.log("Running:", pagination.isRunning());

      PaginationBuilder.stop

    • Chuyển đến trang cuối cùng

      Parameters

      • Optionalinteraction: StringSelectMenuInteraction<CacheType> | ButtonInteraction<CacheType>

        Interaction (button hoặc select menu) nếu có

      Returns Promise<void>

      Promise

      Đây là method helper, thực chất gọi: goto(this.totalPages - 1)

      Dùng khi:

      • Xử lý nút "Last"
      • Nhảy nhanh đến trang cuối
      await pagination.last();
      
      // Jump đến trang cuối sau khi load data
      await pagination.last();

      PaginationBuilder.goto

    • Chuyển sang trang tiếp theo

      Parameters

      • Optionalinteraction: StringSelectMenuInteraction<CacheType> | ButtonInteraction<CacheType>

        Interaction (button hoặc select menu) nếu có

      Returns Promise<void>

      Promise

      Đây là method helper, thực chất gọi: goto(this.page + 1)

      Dùng khi:

      • Xử lý nút "Next"
      • Điều hướng tiến lên 1 trang

      ⚠️ Nếu đang ở trang cuối:

      • Sẽ tự động giữ nguyên (do goto clamp boundary)
      await pagination.next();
      
      // Dùng trong custom button
      onComponent: async ({ interaction, pagination }) => {
      if (interaction.customId === "next") {
      await pagination.next(interaction);
      }
      }

      PaginationBuilder.goto

    • Xử lý lỗi phát sinh trong quá trình thực thi

      Parameters

      • error: unknown

        Đối tượng lỗi nhận được (unknown)

      Returns Promise<any>

      Giá trị trả về từ options.onError nếu được cung cấp, ngược lại sẽ trả về undefined

      Method này đóng vai trò là wrapper để gọi callback onError được truyền vào thông qua options.

      Nếu onError không được định nghĩa, method sẽ không thực hiện bất kỳ xử lý nào và trả về undefined.

      Method không tự xử lý hoặc log lỗi — toàn bộ logic xử lý phụ thuộc vào implementation của options.onError.

      Dùng khi:

      • Chuẩn hoá cách gọi error handler trong class
      • Tách biệt logic xử lý lỗi ra khỏi luồng chính
      • Cho phép inject custom error handler từ bên ngoài

      ⚠️ Không có cơ chế fallback nếu onError không tồn tại. Cần đảm bảo xử lý lỗi ở nơi gọi nếu cần thiết.

      await pagination.onError(new Error("Something went wrong"));
      
      const pagination = new PaginationBuilder({
      onError: (err) => console.error(err),
      });

      await pagination.onError(new Error("Fail"));
      // Không có onError → không có gì xảy ra
      await pagination.onError("Unknown error");

      options.onError

    • Quay lại trang trước

      Parameters

      • Optionalinteraction: StringSelectMenuInteraction<CacheType> | ButtonInteraction<CacheType>

        Interaction (button hoặc select menu) nếu có

      Returns Promise<void>

      Promise

      Đây là method helper, thực chất gọi: goto(this.page - 1)

      Dùng khi:

      • Xử lý nút "Previous"
      • Điều hướng lùi 1 trang

      ⚠️ Nếu đang ở trang đầu:

      • Sẽ tự động giữ nguyên (do goto clamp boundary)
      await pagination.prev();
      
      // Dùng trong custom button
      onComponent: async ({ interaction, pagination }) => {
      if (interaction.customId === "prev") {
      await pagination.prev(interaction);
      }
      }

      PaginationBuilder.goto

    • Chỉ cập nhật lại components (buttons, select menu) của message hiện tại.

      Returns Promise<void>

      Wrapper của reloadPartial với { components: true }.

      Method này:

      • Lấy lại data của page hiện tại
      • Build lại components
      • Gọi message.edit chỉ với components

      Không gọi renderPage, nên:

      • Content / embed giữ nguyên
      • Chỉ thay đổi UI tương tác

      Phù hợp khi:

      • Enable/disable button
      • Thay đổi label / style
      • State nội bộ ảnh hưởng UI
      pagination.state.enabled = !pagination.state.enabled;
      await pagination.refreshComponents();

      Khi:

      • getPageData thất bại
      • buildComponents lỗi
      • message.edit thất bại
      • reloadPartial
      • reload
    • Re-render toàn bộ message (content + components) của trang hiện tại.

      Returns Promise<void>

      Wrapper của reloadPartial với { content: true, components: true }.

      Method này sẽ:

      • Lấy lại data của page hiện tại
      • Render lại nội dung (renderPage)
      • Build lại components
      • Update message thông qua message.edit

      Không thay đổi page hay trạng thái pagination.

      // Reload sau khi clear cache
      pagination.clearCache();
      await pagination.reload();
      // Data thay đổi nhưng vẫn ở cùng page
      await updateDatabase();
      await pagination.reload();

      Khi:

      • getPageData thất bại
      • renderPage lỗi
      • buildComponents lỗi
      • message.edit thất bại
      • reloadPartial
      • refreshComponents
      • reloadContent
    • Re-render lại nội dung (content / embed) của message hiện tại.

      Returns Promise<void>

      Wrapper của reloadPartial với { content: true }.

      Method này:

      • Lấy lại data của page hiện tại
      • Render lại nội dung qua renderPage
      • Update message (không bao gồm components)

      Không thay đổi:

      • Page
      • Components hiện tại

      Phù hợp khi:

      • Data thay đổi
      • Cần cập nhật nội dung nhưng giữ nguyên UI
      await pagination.reloadContent();
      
      pagination.page = 1;
      await pagination.reloadContent();

      Khi:

      • getPageData thất bại
      • renderPage lỗi
      • message.edit thất bại
      • reloadPartial
      • reload
    • Cập nhật một phần message (content và/hoặc components)

      Parameters

      • options: { components?: boolean; content?: boolean }

        Cấu hình phần cần update

        • Optionalcomponents?: boolean

          Cập nhật components (buildComponents)

        • Optionalcontent?: boolean

          Cập nhật nội dung (renderPage)

      Returns Promise<void>

      Promise

      Method này cho phép update riêng lẻ hoặc đồng thời:

      • Nội dung message (content)
      • Components (buttons, select menu,...)

      Flow thực thi:

      1. Nếu không có gì cần update → return sớm
      2. Lấy data hiện tại qua getPageData
      3. Thực thi song song:
        • renderPage (nếu cần content)
        • buildComponents (nếu cần components)
      4. Merge payload hợp lệ
      5. Gọi message.edit để apply thay đổi

      Khi update content:

      • Field components từ renderPage sẽ bị loại bỏ
      • Tránh conflict với components được build riêng

      Dùng khi:

      • Chỉ cần update UI một phần (tối ưu hiệu năng)
      • Tách biệt logic render content và components
      • Tránh re-render toàn bộ message không cần thiết

      ⚠️ Lưu ý:

      • Nếu cả hai option đều false hoặc không truyền → không làm gì
      • renderPagebuildComponents phải trả về dữ liệu hợp lệ
      • message phải còn tồn tại và editable
      // Chỉ update nội dung
      await pagination.reloadPartial({ content: true });
      // Chỉ update components
      await pagination.reloadPartial({ components: true });
      // Update cả hai (chạy song song)
      await pagination.reloadPartial({ content: true, components: true });
      • PaginationBuilder.reloadContent
      • PaginationBuilder.buildComponents
    • Khởi động hệ thống pagination và gửi message ban đầu

      Parameters

      • source: Message<boolean> | ChatInputCommandInteraction<CacheType>

        Message hoặc ChatInputCommandInteraction dùng để reply

      Returns Promise<void>

      Promise

      Đây là method entry point (điểm bắt đầu) của toàn bộ PaginationBuilder.

      Khi gọi method này, hệ thống sẽ:

      1. Lấy dữ liệu của trang đầu tiên (page = 0)
      2. Gọi renderPage để tạo nội dung message
      3. Build components (buttons, select menu,...)
      4. Gửi message (reply)
      5. Tạo InteractionCollector để lắng nghe interaction
      6. Gọi hook onStart (nếu có)

      Method này phải được gọi sau khi bạn đã cấu hình đầy đủ:

      • renderPage (bắt buộc)
      • pages hoặc pageLoader

      Nó hỗ trợ 2 nguồn gọi:

      • Message: dùng trong message command
      • ChatInputCommandInteraction: dùng trong slash command

      ⚠️ Sau khi gọi start, pagination sẽ bắt đầu hoạt động và người dùng có thể tương tác ngay lập tức.

      // Dùng với message command
      await pagination.start(message);
      // Dùng với slash command
      await pagination.start(interaction);
      // Ví dụ đầy đủ
      const pagination = new PaginationBuilder<string[]>({
      totalPages: 5,
      pageLoader: async (page) => {
      return Array.from({ length: 5 }, (_, i) => `Item ${page}-${i}`);
      },
      renderPage: ({ data, page, totalPages }) => ({
      content: `Trang ${page + 1}/${totalPages}\n\n` + data.join("\n")
      }),
      onStart: ({ message }) => {
      console.log("Pagination started:", message.id);
      }
      });

      await pagination.start(interaction);

      Có thể throw nếu:

      • Không có data source (pages hoặc pageLoader)
      • renderPage bị lỗi
      • Gửi message thất bại
    • Dừng pagination và vô hiệu hóa toàn bộ interaction

      Returns Promise<void>

      Promise

      Method này dùng để kết thúc vòng đời của pagination.

      Nó sẽ:

      • Dừng InteractionCollector (ngừng nhận interaction)
      • Xóa toàn bộ components khỏi message (disable UI)
      • Gọi hook onStop (nếu có)

      Dùng khi:

      • Không muốn người dùng tiếp tục tương tác
      • Command đã hoàn tất
      • Cleanup tài nguyên (collector, UI)

      ⚠️ Sau khi gọi stop():

      • Người dùng không thể click button/select menu nữa
      • Pagination được coi là "đã kết thúc"
      • Nếu message.edit thất bại → sẽ được catch và chuyển sang onError
      • Không throw ra ngoài (fail-safe)
      // Dừng pagination thủ công
      await pagination.stop();
      // Dừng sau một hành động custom
      onComponent: async ({ interaction, pagination }) => {
      if (interaction.customId === "confirm") {
      await interaction.reply("Đã xác nhận!");
      await pagination.stop();
      }
      }
      // Auto stop sau timeout custom
      setTimeout(() => {
      pagination.stop();
      }, 30000);
      // Hook onStop
      const pagination = new PaginationBuilder({
      ...options,
      onStop: ({ message }) => {
      console.log("Pagination stopped:", message.id);
      }
      });

      Method này không throw lỗi ra ngoài. Mọi lỗi sẽ được chuyển vào onError.

      PaginationOptions.onError

    • Cập nhật lại toàn bộ danh sách pages (dữ liệu tĩnh) và render lại pagination

      Parameters

      • pages: T[]

        Mảng dữ liệu mới cho từng trang

      Returns Promise<void>

      Promise

      Method này dùng để thay thế hoàn toàn dữ liệu hiện tại (pages) và đồng bộ lại toàn bộ trạng thái pagination.

      Nó sẽ:

      • Xóa cache hiện tại (clearCache)
      • Gán lại pages
      • Cập nhật totalPages
      • Điều chỉnh lại page nếu vượt giới hạn
      • Gọi reload() để render lại message

      Dùng khi:

      • Dữ liệu thay đổi hoàn toàn (refetch, filter, search,...)
      • Chuyển từ dataset cũ sang dataset mới
      • Muốn reset pagination theo data mới

      ⚠️ Method này chỉ áp dụng cho mode pages (data tĩnh). Nếu bạn đang dùng pageLoader, nên xử lý logic ở backend hoặc dùng state + reload.

      • Nếu page hiện tại >= totalPages mới → tự động set về trang cuối (totalPages - 1)
      // Cập nhật dữ liệu mới
      await pagination.updatePages(["A", "B", "C"]);
      // Sau khi filter dữ liệu
      const filtered = allItems.filter(i => i.includes("test"));
      await pagination.updatePages(filtered);
      // Reset data khi search
      pagination.state.keyword = "discord";
      const result = await fetchSearchData();
      await pagination.updatePages(result);
      // Khi data giảm số lượng trang
      // page hiện tại = 5
      // pages mới chỉ có 2 trang
      await pagination.updatePages(["A", "B"]);
      // => page sẽ tự động về 1 (trang cuối)

      Có thể throw nếu:

      • reload() thất bại (renderPage lỗi, Discord API lỗi,...)
      • PaginationBuilder.reload
      • PaginationBuilder.clearCache