From caf422e3d3d35dd8f48d7fd36aed081d779d666b Mon Sep 17 00:00:00 2001
From: timDeHof
Date: Wed, 4 Oct 2023 10:20:33 -0400
Subject: [PATCH 01/12] feat(docs): add pagination tutorial and related files
feat(pagination): add layout, page, and markdoc files for pagination tutorial
feat(pagination): add step-1, step-2, step-3, step-4, step-5, and step-10
markdoc files The changes include adding a new page for the pagination
tutorial in the docs section. Additionally, the necessary layout, page, and
markdoc files are added for each step of the pagination tutorial. This allows
users to learn about pagination in a React app using the Appwrite backend.
The tutorial covers topics such as setting up the project, creating a
database, initializing the Appwrite SDK, and seeding the database with data.
feat(docs): add new files for pagination tutorial steps 6 and 7
The changes include the addition of two new files: `src/routes/docs/tutorials/pagination/step-6/+page.markdoc` and `src/routes/docs/tutorials/pagination/step-7/+page.markdoc`. These files contain the code and instructions for implementing pagination in a React application using Appwrite backend. The `step-6` file adds the initial code for displaying tasks using context and a custom hook, while the `step-7` file adds the implementation for offset pagination and the UI components for navigating through the dataset.
feat(docs): add tutorial pages for implementing cursor pagination and discussing trade-offs
feat(docs): add page for implementing cursor pagination in a React app using Appwrite backend
A new tutorial page has been added to the documentation that explains what cursor pagination is and how to implement it in a React app using the Appwrite backend. The page provides an overview of cursor pagination and its benefits, and includes code examples for implementing cursor pagination using Appwrite's `listDocuments()` command.
feat(docs): add page for discussing the trade-offs between offset pagination and cursor pagination
A new tutorial page has been added to the documentation that discusses the trade-offs between offset pagination and cursor pagination. The page provides a comparison table highlighting the pros and cons of each pagination method, and offers guidance on when to use each method based on different application requirements. The page also includes a recap section summarizing the key points to consider when choosing a pagination method in Appwrite.
---
src/routes/docs/tutorials/+page.svelte | 11 ++
.../docs/tutorials/pagination/+layout.svelte | 10 +
.../docs/tutorials/pagination/+layout.ts | 11 ++
src/routes/docs/tutorials/pagination/+page.ts | 6 +
.../tutorials/pagination/step-1/+page.markdoc | 30 +++
.../pagination/step-10/+page.markdoc | 17 ++
.../tutorials/pagination/step-2/+page.markdoc | 28 +++
.../tutorials/pagination/step-3/+page.markdoc | 57 ++++++
.../tutorials/pagination/step-4/+page.markdoc | 23 +++
.../tutorials/pagination/step-5/+page.markdoc | 63 ++++++
.../tutorials/pagination/step-6/+page.markdoc | 104 ++++++++++
.../tutorials/pagination/step-7/+page.markdoc | 181 ++++++++++++++++++
.../tutorials/pagination/step-8/+page.markdoc | 161 ++++++++++++++++
.../tutorials/pagination/step-9/+page.markdoc | 39 ++++
14 files changed, 741 insertions(+)
create mode 100644 src/routes/docs/tutorials/pagination/+layout.svelte
create mode 100644 src/routes/docs/tutorials/pagination/+layout.ts
create mode 100644 src/routes/docs/tutorials/pagination/+page.ts
create mode 100644 src/routes/docs/tutorials/pagination/step-1/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-10/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-2/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-3/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-4/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-5/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-6/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-7/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-8/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination/step-9/+page.markdoc
diff --git a/src/routes/docs/tutorials/+page.svelte b/src/routes/docs/tutorials/+page.svelte
index 8306de8aff..ede777b7e4 100644
--- a/src/routes/docs/tutorials/+page.svelte
+++ b/src/routes/docs/tutorials/+page.svelte
@@ -50,6 +50,17 @@
+
);
}
```
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-9/+page.markdoc b/src/routes/docs/tutorials/pagination/step-9/+page.markdoc
index aea7266261..67090dfdcb 100644
--- a/src/routes/docs/tutorials/pagination/step-9/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-9/+page.markdoc
@@ -1,7 +1,7 @@
---
layout: tutorial
-title: Trade-offs Discussion
-description: Learn about the trade-offs between each pagination method in a React app using Appwrite backend.
+title: Trade-offs discussion
+description: Learn about the trade-offs between each pagination method in a react app using appwrite backend.
step: 9
---
@@ -10,30 +10,30 @@ step: 9
| Feature / Aspect| Offset Pagination | Cursor Pagination |
|----------------|-----------------------|-----------------------|
| Simplicity | High | Medium to Low |
-| Performance | Good for early pages, degrades with deep pagination |Consistently High |
+| Performance | Good for shallow pagination, degrades with deep pagination |Consistently High |
| Data Consistency | Can be inconsistent with data changes | More consistent |
| Scalability | Good for smaller datasets | High for large datasets |
| Implementation Complexity | Low | Medium to High |
| Direct Page Access | Yes | No |
-Based on the application's requirements, you can then prioritize which features/aspects are most important and make a decision accordingly.
+Based on the application's requirements, you can then determine which features/aspects are most important and make a decision accordingly.
# When to use each method {% #when-to-use-each-method %}
-1. Offset Pagination
- * When you have a relatively small dataset that doesn't change frequently.
+## Offset pagination {% #when-to-use-offset-pagination %}
+ * When you have a small dataset that doesn't change frequently.
* When you need to jump to specific page numbers.
* When simplicity and quick Implementation are priorities.
-2. Cursor Pagination
+## Cursor pagination {% #when-to-use-cursor-pagination %}
* When dealing with large datasets
* For real-time applications or applications with frequently updating datasets.
- * When deep pagination is expected (i.e., users navigating to very high page numbers).
+ * When deep pagination is necessary (i.e., users need to navigate to high page numbers).
* When performance and scalability are critical.
- # Recap {% #recap %}
+# Recap {% #recap %}
* Offset pagination is straightforward and allow direct page access but can suffer from performance issues in deep pagination.
* Cursor pagination offers consistent high performance and is ideal for real-time or large datasets.
- * Data Consistency challenges arise with offset pagination, especially with changing datasets.
- * Cursor pagination can be more complex to implement and doesn't allow easy jumps to specific pages.
+ * Offset pagination causes data consistency issues, especially with changing datasets.
+ * Cursor pagination requires more work and doesn't enable page skips.
* For Appwrite, consider the dataset size, update frequency, and user navigation needs when choosing a pagination method.
\ No newline at end of file
From 648acd178c600fa94fedf9abcc368d3a789d5ab3 Mon Sep 17 00:00:00 2001
From: timDeHof
Date: Thu, 19 Oct 2023 10:35:40 -0400
Subject: [PATCH 03/12] refact(pagination tutorial's steps): rewrote the steps
to be more generalized and focus more on pagination. Fixed title and
description to use sentence case. Changed difficulty to 'intermediate' from
'beginner'. Changed readtime to '20' from '10'. Added tabsto the different
methods to showcase usage of each.
---
src/routes/docs/tutorials/+page.svelte | 11 -
.../tutorials/pagination/step-1/+page.markdoc | 38 +--
.../pagination/step-10/+page.markdoc | 21 +-
.../tutorials/pagination/step-2/+page.markdoc | 6 +-
.../tutorials/pagination/step-3/+page.markdoc | 14 +-
.../tutorials/pagination/step-4/+page.markdoc | 61 +++--
.../tutorials/pagination/step-5/+page.markdoc | 18 +-
.../tutorials/pagination/step-6/+page.markdoc | 70 ++---
.../tutorials/pagination/step-7/+page.markdoc | 243 ++++++++++++------
.../tutorials/pagination/step-8/+page.markdoc | 240 +++++++++++------
.../tutorials/pagination/step-9/+page.markdoc | 42 ++-
11 files changed, 485 insertions(+), 279 deletions(-)
diff --git a/src/routes/docs/tutorials/+page.svelte b/src/routes/docs/tutorials/+page.svelte
index ede777b7e4..8306de8aff 100644
--- a/src/routes/docs/tutorials/+page.svelte
+++ b/src/routes/docs/tutorials/+page.svelte
@@ -50,17 +50,6 @@
-
);
}
@@ -78,9 +162,9 @@ export default App;
### Todos page {% #todos-page %}
-Now, we can craft the Todos page, which will present users with a list of todos-a small chuck of the large dataset.
+Now, we can craft our Todos pages, which will present users with a list of todos-a small chuck of the large dataset in three different ways.
-Create a new file named `src/pages/Todos.jsx` and insert this placeholder code:
+Create four new files in `src/pages/` folder, name them `Todos1`, `Todos2`, `Todos3` and `Todos4`. Then insert this placeholder code in each file:
```js
import { useTodos } from "../lib/context/todos";
@@ -102,5 +186,5 @@ export function Todos() {
);
}
```
-
+we will expand these and add the UI next.
The next steps will involve integrating offset or cursor pagination in our application.
From 2c0b172517e357bbfccf5e20dd74c552702eb6a3 Mon Sep 17 00:00:00 2001
From: timDeHof
Date: Mon, 30 Oct 2023 10:12:01 -0400
Subject: [PATCH 05/12] docs(tutorials): update pagination tutorial to improve
clarity and flow
The tutorial on pagination has been updated to improve the flow and clarity of the instructions. The changes include:
- Replacing the instructions to create new files with instructions to update existing files. This change simplifies the tutorial and makes it easier to follow.
- Adding more detailed explanations of the code changes. This helps readers understand the purpose of each change and how it contributes to the overall functionality of the application.
- Removing redundant sections that were causing confusion. This makes the tutorial more concise and easier to understand.
- Updating the context to use environment variables instead of hard-coded values. This makes the code more flexible and easier to configure.
These changes were made to improve the quality of the tutorial and make it easier for readers to understand and follow.
---
.../tutorials/pagination/step-7/+page.markdoc | 210 +++++++-------
.../tutorials/pagination/step-8/+page.markdoc | 258 +++++++++---------
2 files changed, 228 insertions(+), 240 deletions(-)
diff --git a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
index de897b8d34..fa9ca525ed 100644
--- a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
@@ -25,34 +25,103 @@ this will retrieve the next four tasks, starting at task-5
We'll explore different UI patterns for offset pagination and how to implement them in our React app.
-# Creating the Pagination Component {% #creating-the-pagination-component %}
+# Adding the Pagination Component {% #Adding-the-pagination-component %}
{% tabs %}
{% tabsitem #Basic-pagination title="Basic Pagination" %}
-Create a file and named `src/components/BasicPagination.jsx`, insert the following code:
+Open the file named `src/pages/Todos1.jsx`, overwrite it with the following code:
```js
-import React from 'react';
+import { useCallback } from "react";
+import { useTodos } from "../lib/context/todos";
function BasicPagination(props) {
const { loadPrev, loadNext, hasNext, hasPrev } = props;
+ const debounce = (func, delay) => {
+ let debounceTimer;
+ return (...args) => {
+ clearTimeout(debounceTimer);
+ debounceTimer = setTimeout(() => func(...args), delay);
+ };
+ };
+
+ const debouncedLoadPrev = useCallback(debounce(loadPrev, 300), [loadPrev]);
+
+ const debouncedLoadNext = useCallback(debounce(loadNext, 300), [loadNext]);
+
return (
+ 1}
+ />
+
+ );
+}
+export default TodosWithBasicPagination;
```
This component will only have "Previous" and "Next" buttons.
{% /tabsitem %}
{% tabsitem #numbered-pagination title="Numbered Pagination" %}
-Create a file and named `src/components/NumberedPagination.jsx`, insert the following code:
+Open the file named `src/pages/Todos2.jsx`, overwrite it with the following code:
```js
-import React from 'react';
+import { useCallback } from "react";
+import { useTodos } from "../lib/context/todos";
+
+export function TodosWithBasicPaginationAndNumbers() {
+ const todos = useTodos();
+
+ return (
+
+
);
}
-
-export default NumberedPagination;
```
This component will have "Previous" and "Next" buttons along with numbered pages for direct navigation.
@@ -93,32 +175,26 @@ This component will have "Previous" and "Next" buttons along with numbered pages
# Update the todos context {% #update-the-todos-context %}
-Update the contents of `src/lib/context/todos.jsx` with the following code, replacing `[YOUR_DATABASE_ID]` and `[YOUR_COLLECTION_ID]` with your project ID and collection ID.
+Update the contents of `src/lib/context/todos.jsx` with the following code:
```js
import { createContext, useContext, useEffect, useState } from "react";
import { databases } from "../appwrite";
import { Query } from "appwrite";
-const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]";
-const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]";
-
const TodosContext = createContext();
export function useTodos() {
return useContext(TodosContext);
}
-export function TodosProvider(props) {
+export function OffsetProvider(props) {
const [todos, setTodos] = useState([]);
const [offset, setOffset] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
- const [counts, setCounts] = useState({
- totalPages: 500,
- totalResults: 1000,
- });
+ const [totalPages, setTotalPages] = useState(0);
const pageLimit = 4;
- const hasNext = counts.totalPages > currentPage;
+ const hasNext = totalPages > currentPage;
async function loadNext() {
if (hasNext) {
@@ -139,8 +215,8 @@ export function TodosProvider(props) {
async function init() {
try {
const response = await databases.listDocuments(
- TODOS_DATABASE_ID,
- TODOS_COLLECTION_ID,
+ import.meta.env.APPWRITE_DATABASE_ID,
+ import.meta.env.APPWRITE_COLLECTION_ID,
[
Query.orderAsc("$createdAt"),
Query.limit(pageLimit),
@@ -150,10 +226,7 @@ export function TodosProvider(props) {
setTodos(response.documents);
setCurrentPage(Math.floor(offset / pageLimit) + 1);
- setCounts({
- totalPages: response.total / pageLimit,
- totalResults: response.total,
- });
+ setTotalPages(response.total / pageLimit);
} catch (error) {
console.log(error);
}
@@ -170,7 +243,7 @@ export function TodosProvider(props) {
loadPrev,
loadPage,
currentPage,
- totalPages: counts.totalPages,
+ totalPages,
hasNext,
hasPrev: currentPage > 1,
}}>
@@ -182,81 +255,10 @@ export function TodosProvider(props) {
In this updated context:
* We're using `Query.limit()` to set the number of tasks we want to fetch per page.
* We're using `Query.offset()` to set the starting point for fetching tasks based on the current page.
- * We've added `loadNextPage`, `loadPrevPage`, and `loadPage` function to handle pagination.
+ * We've added `loadNext`, `loadPrev`, and `loadPage` function to handle pagination.
Now, you can use this context in your components to display todo items and handle pagination using Appwrite's offset pagination methods.
-# Update the Todos page {% #update-the-todoss-page %}
-
-Now, let's use our Pagination component in the Todos page.
-{% tabs %}
-{% tabsitem #Basic-pagination title="Basic Pagination" %}
-Update the Todos page in `src/pages/Todos.jsx`:
-```js
-import BasicPagination from '../components/BasicPagination';
-import {useTodos} from "../lib/context/todos";
-export function TodosWithBasicPagination() {
- const todos = useTodos();
-
- return (
-
-
Todos
-
- {todos.current.map(todo => (
-
- {`Task-${todo.taskId}: `}
-
-
-
- ))}
-
- 1}
- />
-
- );
-}
-```
-{% /tabsitem %}
-{% tabsitem #numbered-pagination title="Numbered Pagination" %}
-Update the Todos page in `src/pages/Todos.jsx`:
-```js
-import NumberedPagination from '../components/NumberedPagination';
-
-export function TasksWithNumberedPagination() {
- const tasks = useTasks();
-
- return (
-
-
Todos
-
- {tasks.current.map(task => (
-
- {`Task-${task.taskId}: `}
-
-
-
- ))}
-
- 1}
- />
-
- );
-}
-```
-{% /tabsitem %}
-{% /tabs %}
-
You can now choose between the two offset pagination styles based on your UI/UX preferences.
In the next step, we'll transition from our current pagination method to cursor pagination method.
diff --git a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
index fea4ad9cb6..868db95aa4 100644
--- a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
@@ -27,161 +27,210 @@ Cursor pagination is typically used in scenarios where the dataset is large, and
{% tabs %}
{% tabsitem #Basic-cursor-pagination title="Basic Cursor Pagination" %}
-Create a file and named `src/components/BasicCursorPagination.jsx`, insert the following code:
+Open the file named `src/pages/Todos3.jsx`, overwrite the content with the following code:
```js
+import React, { useCallback } from "react";
+import { useTodos } from "../lib/context/todos2";
+export function TodosWithBasicCursorPagination() {
+ const todos = useTodos();
+
+ return (
+
+
);
}
-export default CursorPagination;
-
```
-This is the most straightforward use of cursor pagination. You have "Load Previous" and "Load More" buttons, and you fetch data based on the last or first items's cursor.
+This is the most straightforward use of cursor pagination. You have "Load More" button,which will fetch more data based on the last item's cursor.
{% /tabsitem %}
-{% tabsitem #infinite-scroll-with-cursor title="Infinite Scroll with Cursor" %}
-Create a file and named `src/components/InfiniteScroll.jsx`, insert the following code:
+{% tabsitem #Bidirectional-cursor-pagination title="Bidirectional Cursor Pagination" %}
+Open the file named `src/pages/Todos4.jsx`, overwrite the content with the following code:
```js
-import React, { useEffect } from 'react';
+import React, { useCallback } from "react";
+import { useTodos } from "../lib/context/todos2";
-function InfiniteScroll(props) {
- const {
- hasNext,
- loadMore,
- current
- } = props;
+export function TodosWithBidirectionalPagination() {
+ const todos = useTodos();
- useEffect(() => {
- const handleScroll = () => {
- // Check if we're at the bottom of the viewport
- if (window.innerHeight + document.documentElement.scrollTop !== document.documentElement.offsetHeight) return;
- if (hasNext && current && current.length) {
- loadMore();
- }
- };
+ return (
+
+
);
}
-
-export default InfiniteScroll;
```
-Instead of manually clicking a "load more" button, you can automatically fetch more items when the user scrolls to the bottom of the list. This provides a seamless experience for users.
+Bidirectional cursor pagination allows users to navigate through datasets both forwards and backwards using a cursor to maintain position, providing greater flexibility in exploring data. On the other hand, regular cursor pagination typically only supports forward navigation through the dataset, limiting the user's ability to easily access or revisit previous data entries.
{% /tabsitem %}
{% /tabs %}
# Update todos context {% #updating-todos-context %}
-Update the contents of `src/lib/context/todos.jsx` with the following code, replacing `[YOUR_DATABASE_ID]` and `[YOUR_COLLECTION_ID]` with your project ID and collection ID.
+Update the contents of `src/lib/context/todos2.jsx` with the following code:
```js
import { createContext, useContext, useEffect, useState } from "react";
import { databases } from "../appwrite";
import { Query } from "appwrite";
-const TODOS_DATABASE_ID = "[YOUR_DATABASE_ID]";
-const TODOS_COLLECTION_ID = "[YOUR_COLLECTION_ID]";
-
const TodosContext = createContext();
export function useTodos() {
return useContext(TodosContext);
}
-export function TodosProvider(props) {
+export function TodosProvider2(props) {
const [todos, setTodos] = useState([]);
const [lastId, setLastId] = useState(null);
const [firstId, setFirstId] = useState(null);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
+ const [loading, setLoading] = useState(false);
const pageLimit = 4;
- const hasNext = totalPages > currentPage;
+ const hasMore = totalPages > currentPage;
async function loadMore() {
- if (currentPage < totalPages) {
- try {
+ if (todos.length === 0 || (hasMore && !loading)) {
+ setLoading(true);
+ try {
const response = await databases.listDocuments(
- TODOS_DATABASE_ID,
- TODOS_COLLECTION_ID,
+ import.meta.env.APPWRITE_DATABASE_ID,
+ import.meta.env.APPWRITE_COLLECTION_ID,
[
- Query.orderDesc("$createdAt"),
+ Query.orderAsc("$createdAt"),
Query.limit(pageLimit),
Query.cursorAfter(lastId),
],
);
- setCurrentPage(currentPage + 1)
+ setCurrentPage((prevPage) => prevPage + 1);
setFirstId(response.documents[0].$id);
setLastId(response.documents[response.documents.length - 1].$id);
- setTodos([...todos, ...response.documents]);
- } catch (error) {
- console.log("Error loading more todos:", error);
- }
+ setTodos((prevTodos) => [...prevTodos, ...response.documents]);
+ } catch (error) {
+ console.log("Error loading more todos:", error);
+ }
+ setLoading(false);
}
}
+
async function loadPrev() {
- if (currentPage > 1) {
- try {
+ if (currentPage > 1) {
+ try {
const response = await databases.listDocuments(
- TODOS_DATABASE_ID,
- TODOS_COLLECTION_ID,
+ import.meta.env.APPWRITE_DATABASE_ID,
+ import.meta.env.APPWRITE_COLLECTION_ID,
[
Query.orderAsc("$createdAt"),
Query.limit(pageLimit),
Query.cursorBefore(firstId),
],
);
- setCurrentPage(currentPage -1 );
+ setCurrentPage(currentPage - 1);
setFirstId(response.documents[0].$id);
setLastId(response.documents[response.documents.length - 1].$id);
- setTasks(response.documents);
- } catch (error) {
- console.error("Error loading previous todos:", error);
+ setTodos(response.documents);
+ } catch (error) {
+ console.error("Error loading previous todos:", error);
+ }
}
}
- }
async function init() {
+ setLoading(true);
try {
const response = await databases.listDocuments(
- TODOS_DATABASE_ID,
- TODOS_COLLECTION_ID,
+ import.meta.env.APPWRITE_DATABASE_ID,
+ import.meta.env.APPWRITE_COLLECTION_ID,
[Query.orderAsc("$createdAt"), Query.limit(pageLimit)],
);
setFirstId(response.documents[0].$id);
setLastId(response.documents[response.documents.length - 1].$id);
- setTasks(response.documents);
+ setTodos(response.documents);
setTotalPages(Math.ceil(response.total / pageLimit));
} catch (error) {
console.log("Error initializing tasks:", error);
}
+ setLoading(false);
}
useEffect(() => {
init();
- }, []);
+ }, [pageLimit]);
return (
{props.children}
);
}
+
```
In this updated context:
* We're using `Query.cursorAfter()` is used to fetch documents that come after a specific document.
@@ -205,71 +256,6 @@ In this updated context:
* We's added `firstId` to hold the ID of the first document in the current set of todos. By using this ID with `Query.cursorBefore()`, we're telling Appwrite: "Give me the set of todos that come before this specific todo item."
Now, you can use this context in your components to display todo items and handle pagination using Appwrite's cursor pagination methods.
-# Update Todos page {% #update-todos-page %}
-Now, let's use our Pagination component in the Todos Page.
-{% tabs %}
-{% tabsitem #Basic-cursor-pagination title="Basic Cursor Pagination" %}
-Update the Todos page in `src/pages/Todos.jsx`:
-```js
-import React from 'react';
-import { useTodos } from '../lib/context/todos';
-import BasicCursorPagination from '../components/BasicCursorPagination';
-
-export function TodosPage() {
- const todos = useTodos();
-
- return (
-
-
-
-
- );
-}
-```
-This implementation provide a basic pagination mechanism to navigate through the dataset and allows users to move to the next set of todo items or go back to the previous set.
-{% /tabsitem %}
-{% tabsitem #infinite-scroll-with-cursor title="Infinite Scroll with Cursor" %}
-Update the Todos page in `src/pages/Todos.jsx`:
-```js
-import InfiniteScroll from '../components/InfiniteScroll';
-import { useTodos } from '../lib/context/todos';
-
-export function Todos() {
- const todos = useTodos();
-
- return (
-
-
-
- );
-}
-```
-This implementation will automatically load more tasks when the user scrolls to the bottom of the page, as long as there are more tasks to load (as indicated by the hasNext value from the context).
-{% /tabsitem %}
-{% /tabs %}
# See it in action {% #see-it-in-action %}
-Now's the perfect time to take a moment to run your application and see cursor pagination in action. Navigate through the tasks by scrolling or using the page navigation. As you scroll, you'll notice the seamless loading of todo items, thanks to infinite scrolling. Additionally, using the "Load Previous" and "Load More" buttons, you can manually navigate through tasks, showcasing the eficiency of cursor pagination.This approach ensures a smoother user experience by fetching only a subset of tod items, reducing server load and enhancing responsiveness.
+Now's the perfect time to take a moment to run your application and see cursor pagination in action. Navigate using the "Load Previous" and "Load More" buttons, you can manually navigate through tasks, showcasing the efficiency of cursor pagination.This approach ensures a smoother user experience by fetching only a subset of todo items, reducing server load and enhancing responsiveness.
From a0a4a68948e9644aada7df1fd7b40e5c6ed25373 Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Mon, 30 Oct 2023 15:17:11 -0400
Subject: [PATCH 06/12] Apply suggestions from code review
---
.../docs/tutorials/pagination/step-2/+page.markdoc | 2 +-
.../docs/tutorials/pagination/step-4/+page.markdoc | 8 ++++----
.../docs/tutorials/pagination/step-5/+page.markdoc | 14 +++++---------
3 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/src/routes/docs/tutorials/pagination/step-2/+page.markdoc b/src/routes/docs/tutorials/pagination/step-2/+page.markdoc
index ceba13e534..d3b486bdba 100644
--- a/src/routes/docs/tutorials/pagination/step-2/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-2/+page.markdoc
@@ -18,7 +18,7 @@ npm create vite@latest todo-app -- --template react && cd todo-app
Install the JavaScript Appwrite Web SDK.
```sh
-npm install appwrite
+npm install appwrite dotenv
```
You can start the development server to watch your app update in the browser as you make changes.
diff --git a/src/routes/docs/tutorials/pagination/step-4/+page.markdoc b/src/routes/docs/tutorials/pagination/step-4/+page.markdoc
index 69b8bd0e6e..42afc2cbc3 100644
--- a/src/routes/docs/tutorials/pagination/step-4/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-4/+page.markdoc
@@ -50,8 +50,8 @@ When setting up your collection, make sure it has these attributes:
# Add Permissions {% #add-permissions %}
1. Go to the **Settings** tab.
-2. Scroll to the 'Permissions' section.
-3. Add the `Any` role, ensuring you select both `CREATE` and `READ` permissions.
-{% info title="Tutorial only" %}
- Don't set the **Any** role with both 'CREATE' and 'READ' in production. This is only for demonstration purposes for this tutorial.
+2. Scroll to the **Permissions** section.
+3. Add the **Any** role, ensuring you select both **CREATE** and **READ** permissions.
+{% info title="Any role" %}
+ The **Any** role is used in this tutorial for demonstration purposes. Remember to protect your user's data in production applications by configuring proper [permissions](/docs/advanced/platform/permissions).
{% /info %}
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-5/+page.markdoc b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
index 64cb8d75b5..04a2727748 100644
--- a/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
@@ -5,28 +5,24 @@ description: Setup the environment for seeding the database
step: 5
---
# Create environment configuration file {% #create-env-file %}
-Now, let's set up the environment configuration for our project.
-Follow these steps to create a `.env` file in the root directory of the project.
-1. Navigate to the root of the project using your terminal or file explorer.
-2. Create a new file named `.env` (make sure that there is no file extension like `.txt`).
-3. Open the `.env` file with your preferred text editor.
-4. Add the necessary environment variables and their values for your respective project, database, and collection IDs in the format `KEY=VALUE`. Each variable should be on a new line.
+Set up the environment configuration for your project to declare project-wide variables like Appwrite project ID, database ID, and collection ID. Create a `.env` and add the declare variables.
```sh
APPWRITE_PROJECT_ID=[YOUR_PROJECT_ID]
APPWRITE_DATABASE_ID=[YOUR_DATABASE_ID]
APPWRITE_COLLECTION_ID=[YOUR_COLLECTION_ID]
-```
-5. Save the file.
This `.env` file will now act as the central place for your project's environment-specific configurations. Make sure not to sure this file publicly, especially if it contains sensitive information like API keys or database credentials.
# Seeding the collection {% #seeding-the-collection %}
We'll now create a script that populates documents from a `setup.json` file.Create a new file `./db/setup.js` in the root of your project directory for this purpose.
-Insert the following code, make sure that you have `[APPWRITE_DATABASE_ID]` and `[APPWRITE_COLLECTION_ID]` with your respective database and collection IDs in a .env file on the root.
+Insert the following code, make sure that you have `[APPWRITE_DATABASE_ID]` and `[APPWRITE_COLLECTION_ID]` with your respective database and collection IDs in a `.env` file on the root.
```js
import { databases } from "../src/lib/appwrite.js";
import { ID } from "appwrite";
+import dotenv from "dotenv";
+
+dotenv.config();
const dataURL = "https://jsonplaceholder.typicode.com/users/1/todos";
From f6999c653ff7868f62b60732205f3d128351825c Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Mon, 30 Oct 2023 15:26:18 -0400
Subject: [PATCH 07/12] Apply suggestions from code review
---
.../tutorials/pagination/step-6/+page.markdoc | 18 +++++++++---------
.../tutorials/pagination/step-7/+page.markdoc | 4 ++--
.../tutorials/pagination/step-8/+page.markdoc | 10 +++++-----
3 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/src/routes/docs/tutorials/pagination/step-6/+page.markdoc b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
index 3df89902c8..e6279ecaf7 100644
--- a/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
@@ -8,7 +8,7 @@ step: 6
We'll now display our Todos on the page. In React, [context](https://reactjs.org/docs/context.html) allows us to share data across components. We'll leverage context and create two custom hooks to manage our todos. One for each pagination method.
-Create a new file named `src/lib/context/todos.jsx` and insert the following code:
+Create a new file named `src/lib/context/todos-offset-pagination.jsx` and insert the following code:
```js
import { createContext, useContext, useEffect, useState } from "react";
@@ -48,7 +48,7 @@ export function OffsetProvider(props) {
);
}
```
-Also create a new file named `src/lib/context/todos2.jsx` and insert the following code:
+Also create a new file named `src/lib/context/todos-cursor-pagination.jsx` and insert the following code:
```js
import { createContext, useContext, useEffect, useState } from "react";
@@ -98,12 +98,12 @@ Update `src/App.jsx` with the following code:
```js
import React from "react";
-import { OffsetProvider } from "./lib/context/todos";
-import { CursorProvider } from "./lib/context/todos2";
-import { TodosWithBasicPagination } from "./pages/Todos1";
-import { TodosWithBasicPaginationAndNumbers } from "./pages/Todos2";
-import { TodosWithBasicCursorPagination } from "./pages/Todos3";
-import { TodosWithBidirectionalPagination } from "./pages/Todos4";
+import { OffsetProvider } from "./lib/context/todos-offset-pagination";
+import { CursorProvider } from "./lib/context/todos-cursor-pagination";
+import { TodosWithBasicPagination } from "./pages/TodosWithBasicPagination";
+import { TodosWithBasicPaginationAndNumbers } from "./pages/TodosWithBasicPaginationAndNumbers";
+import { TodosWithBasicCursorPagination } from "./pages/TodosWithBasicCursorPagination";
+import { TodosWithBidirectionalPagination } from "./pages/TodosWithBidirectionalPagination";
const Header = () => (
Basic Offset Pagination
@@ -164,7 +164,7 @@ export default App;
Now, we can craft our Todos pages, which will present users with a list of todos-a small chuck of the large dataset in three different ways.
-Create four new files in `src/pages/` folder, name them `Todos1`, `Todos2`, `Todos3` and `Todos4`. Then insert this placeholder code in each file:
+Create four new files in `src/pages/` folder, name them `TodosWithBasicPagination`, `TodosWithBasicPaginationAndNumbers`, `TodosWithBasicCursorPagination`, and `TodosWithBidirectionalPagination `. Then insert this placeholder code in each file:
```js
import { useTodos } from "../lib/context/todos";
diff --git a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
index fa9ca525ed..f7ab7c1bcf 100644
--- a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
@@ -29,7 +29,7 @@ We'll explore different UI patterns for offset pagination and how to implement t
{% tabs %}
{% tabsitem #Basic-pagination title="Basic Pagination" %}
-Open the file named `src/pages/Todos1.jsx`, overwrite it with the following code:
+Open the file named `src/pages/TodosWithBasicPagination.jsx`, overwrite it with the following code:
```js
import { useCallback } from "react";
import { useTodos } from "../lib/context/todos";
@@ -90,7 +90,7 @@ export default TodosWithBasicPagination;
This component will only have "Previous" and "Next" buttons.
{% /tabsitem %}
{% tabsitem #numbered-pagination title="Numbered Pagination" %}
-Open the file named `src/pages/Todos2.jsx`, overwrite it with the following code:
+Open the file named `src/pages/TodosWithBasicPaginationAndNumbers.jsx`, overwrite it with the following code:
```js
import { useCallback } from "react";
import { useTodos } from "../lib/context/todos";
diff --git a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
index 868db95aa4..98af1acfe2 100644
--- a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
@@ -27,10 +27,10 @@ Cursor pagination is typically used in scenarios where the dataset is large, and
{% tabs %}
{% tabsitem #Basic-cursor-pagination title="Basic Cursor Pagination" %}
-Open the file named `src/pages/Todos3.jsx`, overwrite the content with the following code:
+Open the file named `src/pages/TodosWithBasicCursorPagination.jsx`, overwrite the content with the following code:
```js
import React, { useCallback } from "react";
-import { useTodos } from "../lib/context/todos2";
+import { useTodos } from "../lib/context/todos-cursor-pagination";
export function TodosWithBasicCursorPagination() {
const todos = useTodos();
@@ -81,10 +81,10 @@ function CursorPagination(props) {
This is the most straightforward use of cursor pagination. You have "Load More" button,which will fetch more data based on the last item's cursor.
{% /tabsitem %}
{% tabsitem #Bidirectional-cursor-pagination title="Bidirectional Cursor Pagination" %}
-Open the file named `src/pages/Todos4.jsx`, overwrite the content with the following code:
+Open the file named `src/pages/TodosWithBidirectionalPagination.jsx`, overwrite the content with the following code:
```js
import React, { useCallback } from "react";
-import { useTodos } from "../lib/context/todos2";
+import { useTodos } from "../lib/context/todos-cursor-pagination";
export function TodosWithBidirectionalPagination() {
const todos = useTodos();
@@ -143,7 +143,7 @@ Bidirectional cursor pagination allows users to navigate through datasets both f
# Update todos context {% #updating-todos-context %}
-Update the contents of `src/lib/context/todos2.jsx` with the following code:
+Update the contents of `src/lib/context/todos-cursor-pagination.jsx` with the following code:
```js
import { createContext, useContext, useEffect, useState } from "react";
From 55b349114c56d004eaf594062a1547b31f95accd Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Mon, 30 Oct 2023 15:27:58 -0400
Subject: [PATCH 08/12] Apply suggestions from code review
---
src/routes/docs/tutorials/pagination/step-10/+page.markdoc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/routes/docs/tutorials/pagination/step-10/+page.markdoc b/src/routes/docs/tutorials/pagination/step-10/+page.markdoc
index 5b7137a432..c7648bf6ed 100644
--- a/src/routes/docs/tutorials/pagination/step-10/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-10/+page.markdoc
@@ -20,4 +20,4 @@ Before deploying or integrating your pagination into a larger project, it's cruc
To deepen your understanding and explore more advanced pagination techniques or nuances, refer to the official documentation:
[Appwrite pagination documentation](/docs/products/databases/pagination).
-Remember, the choice between offset and cursor pagination will depend on your application's specific needs. Always consider the size of the dataset, usr experience, and performance requirements when making your decision.
\ No newline at end of file
+Remember, the choice between offset and cursor pagination will depend on your application's specific needs. Always consider the size of the dataset, user experience, and performance requirements when making your decision.
\ No newline at end of file
From e1105cba842b11434f8490ab68acf0599ac5c84e Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Mon, 30 Oct 2023 15:29:37 -0400
Subject: [PATCH 09/12] Update
src/routes/docs/tutorials/pagination/step-5/+page.markdoc
---
src/routes/docs/tutorials/pagination/step-5/+page.markdoc | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/routes/docs/tutorials/pagination/step-5/+page.markdoc b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
index 04a2727748..7d9b06c782 100644
--- a/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
@@ -10,6 +10,7 @@ Set up the environment configuration for your project to declare project-wide va
APPWRITE_PROJECT_ID=[YOUR_PROJECT_ID]
APPWRITE_DATABASE_ID=[YOUR_DATABASE_ID]
APPWRITE_COLLECTION_ID=[YOUR_COLLECTION_ID]
+```
This `.env` file will now act as the central place for your project's environment-specific configurations. Make sure not to sure this file publicly, especially if it contains sensitive information like API keys or database credentials.
# Seeding the collection {% #seeding-the-collection %}
From 95391d9b5ef478dd3858f8b663b22666bdf57014 Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Mon, 30 Oct 2023 16:03:23 -0400
Subject: [PATCH 10/12] Apply suggestions from code review
---
.../docs/tutorials/pagination/step-6/+page.markdoc | 14 +++++++-------
.../docs/tutorials/pagination/step-7/+page.markdoc | 10 +++++-----
.../docs/tutorials/pagination/step-8/+page.markdoc | 4 ++--
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/routes/docs/tutorials/pagination/step-6/+page.markdoc b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
index e6279ecaf7..07c2e378fd 100644
--- a/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
@@ -100,10 +100,10 @@ Update `src/App.jsx` with the following code:
import React from "react";
import { OffsetProvider } from "./lib/context/todos-offset-pagination";
import { CursorProvider } from "./lib/context/todos-cursor-pagination";
-import { TodosWithBasicPagination } from "./pages/TodosWithBasicPagination";
-import { TodosWithBasicPaginationAndNumbers } from "./pages/TodosWithBasicPaginationAndNumbers";
+import { TodosWithOffsetPagination } from "./pages/TodosWithOffsetPagination";
+import { TodosWithOffsetPaginationAndNumbers } from "./pages/TodosWithOffsetPaginationAndNumbers";
import { TodosWithBasicCursorPagination } from "./pages/TodosWithBasicCursorPagination";
-import { TodosWithBidirectionalPagination } from "./pages/TodosWithBidirectionalPagination";
+import { TodosWithBidirectionalCursorPagination } from "./pages/TodosWithBidirectionalCursorPagination";
const Header = () => (
Basic Offset Pagination
@@ -118,13 +118,13 @@ const Header = () => (
function App() {
const showBasicOffsetPagination = () => {
if (window.location.pathname === "/basicOffsetPagination") {
- return ;
+ return ;
}
};
const showOffsetPaginationWithPageNumbers = () => {
if (window.location.pathname === "/offsetPaginationWithPageNumbers") {
- return ;
+ return ;
}
};
@@ -136,7 +136,7 @@ function App() {
const showBidirecionalPagination = () => {
if (window.location.pathname === "/bidirectionalPagination") {
- return ;
+ return ;
}
};
@@ -164,7 +164,7 @@ export default App;
Now, we can craft our Todos pages, which will present users with a list of todos-a small chuck of the large dataset in three different ways.
-Create four new files in `src/pages/` folder, name them `TodosWithBasicPagination`, `TodosWithBasicPaginationAndNumbers`, `TodosWithBasicCursorPagination`, and `TodosWithBidirectionalPagination `. Then insert this placeholder code in each file:
+Create four new files in `src/pages/` folder, name them `TodosWithOffsetPagination`, `TodosWithOffsetPaginationAndNumbers`, `TodosWithBasicCursorPagination`, and `TodosWithBidirectionalCursorPagination `. Then insert this placeholder code in each file:
```js
import { useTodos } from "../lib/context/todos";
diff --git a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
index f7ab7c1bcf..2db6c4f88e 100644
--- a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
@@ -29,7 +29,7 @@ We'll explore different UI patterns for offset pagination and how to implement t
{% tabs %}
{% tabsitem #Basic-pagination title="Basic Pagination" %}
-Open the file named `src/pages/TodosWithBasicPagination.jsx`, overwrite it with the following code:
+Open the file named `src/pages/TodosWithOffsetPagination.jsx`, overwrite it with the following code:
```js
import { useCallback } from "react";
import { useTodos } from "../lib/context/todos";
@@ -61,7 +61,7 @@ function BasicPagination(props) {
);
}
-function TodosWithBasicPagination() {
+function TodosWithOffsetPagination() {
const todos = useTodos();
return (
@@ -85,17 +85,17 @@ function TodosWithBasicPagination() {
);
}
-export default TodosWithBasicPagination;
+export default TodosWithOffsetPagination;
```
This component will only have "Previous" and "Next" buttons.
{% /tabsitem %}
{% tabsitem #numbered-pagination title="Numbered Pagination" %}
-Open the file named `src/pages/TodosWithBasicPaginationAndNumbers.jsx`, overwrite it with the following code:
+Open the file named `src/pages/TodosWithOffsetPaginationAndNumbers.jsx`, overwrite it with the following code:
```js
import { useCallback } from "react";
import { useTodos } from "../lib/context/todos";
-export function TodosWithBasicPaginationAndNumbers() {
+export function TodosWithOffsetPaginationAndNumbers() {
const todos = useTodos();
return (
diff --git a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
index 98af1acfe2..c75f43f252 100644
--- a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
+++ b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
@@ -81,12 +81,12 @@ function CursorPagination(props) {
This is the most straightforward use of cursor pagination. You have "Load More" button,which will fetch more data based on the last item's cursor.
{% /tabsitem %}
{% tabsitem #Bidirectional-cursor-pagination title="Bidirectional Cursor Pagination" %}
-Open the file named `src/pages/TodosWithBidirectionalPagination.jsx`, overwrite the content with the following code:
+Open the file named `src/pages/TodosWithBidirectionalCursorPagination.jsx`, overwrite the content with the following code:
```js
import React, { useCallback } from "react";
import { useTodos } from "../lib/context/todos-cursor-pagination";
-export function TodosWithBidirectionalPagination() {
+export function TodosWithBidirectionalCursorPagination() {
const todos = useTodos();
return (
From 25b506b12d2e333ab7bd82275e4f6fd6370d2d4f Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Tue, 31 Oct 2023 00:13:06 +0000
Subject: [PATCH 11/12] Add some changes to fix functionality of tutorial and
use better names
---
.../docs/tutorials/pagination/+layout.svelte | 10 -
.../docs/tutorials/pagination/+layout.ts | 11 -
src/routes/docs/tutorials/pagination/+page.ts | 6 -
.../tutorials/pagination/step-1/+page.markdoc | 32 ---
.../pagination/step-10/+page.markdoc | 23 --
.../tutorials/pagination/step-2/+page.markdoc | 28 --
.../tutorials/pagination/step-3/+page.markdoc | 60 ----
.../tutorials/pagination/step-4/+page.markdoc | 57 ----
.../tutorials/pagination/step-5/+page.markdoc | 67 -----
.../tutorials/pagination/step-6/+page.markdoc | 190 -------------
.../tutorials/pagination/step-7/+page.markdoc | 267 ------------------
.../tutorials/pagination/step-8/+page.markdoc | 261 -----------------
.../tutorials/pagination/step-9/+page.markdoc | 35 ---
13 files changed, 1047 deletions(-)
delete mode 100644 src/routes/docs/tutorials/pagination/+layout.svelte
delete mode 100644 src/routes/docs/tutorials/pagination/+layout.ts
delete mode 100644 src/routes/docs/tutorials/pagination/+page.ts
delete mode 100644 src/routes/docs/tutorials/pagination/step-1/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-10/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-2/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-3/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-4/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-5/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-6/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-7/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-8/+page.markdoc
delete mode 100644 src/routes/docs/tutorials/pagination/step-9/+page.markdoc
diff --git a/src/routes/docs/tutorials/pagination/+layout.svelte b/src/routes/docs/tutorials/pagination/+layout.svelte
deleted file mode 100644
index fb9fb3980f..0000000000
--- a/src/routes/docs/tutorials/pagination/+layout.svelte
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/src/routes/docs/tutorials/pagination/+layout.ts b/src/routes/docs/tutorials/pagination/+layout.ts
deleted file mode 100644
index 562b11506f..0000000000
--- a/src/routes/docs/tutorials/pagination/+layout.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { LayoutLoad } from './$types';
-
-export const load: LayoutLoad = ({ url }) => {
- const tutorials = import.meta.glob('./**/*.markdoc', {
- eager: true
- });
- return {
- tutorials,
- pathname: url.pathname
- };
-};
diff --git a/src/routes/docs/tutorials/pagination/+page.ts b/src/routes/docs/tutorials/pagination/+page.ts
deleted file mode 100644
index 1726993fa1..0000000000
--- a/src/routes/docs/tutorials/pagination/+page.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { redirect } from '@sveltejs/kit';
-import type { PageLoad } from './$types';
-
-export const load: PageLoad = async () => {
- throw redirect(303, '/docs/tutorials/pagination/step-1');
-};
diff --git a/src/routes/docs/tutorials/pagination/step-1/+page.markdoc b/src/routes/docs/tutorials/pagination/step-1/+page.markdoc
deleted file mode 100644
index 4443ddcc61..0000000000
--- a/src/routes/docs/tutorials/pagination/step-1/+page.markdoc
+++ /dev/null
@@ -1,32 +0,0 @@
----
-layout: tutorial
-title: Implementing pagination with Appwrite and React
-description: Dive deep into integrating pagination in a React application using the Appwrite backend
-step: 1
-difficulty: intermediate
-readtime: 20
----
-
- In this tutorial, you'll learn how to seamlessly integrate Appwrite's pagination methods into a React application. Proper pagination ensures efficient data display, enhance user experience, and provide a smooth interface for navigating large datasets.
-
-# Why pagination? {% #why-pagination %}
-Pagination is critical in web development, especially when working with huge databases. It helps users explore and process data without feeling overwhelmed by breaking it down into smaller parts. Data-rich applications like search engines and e-commerce sites require this.
-
-The benefits are numerous:
-* faster load times
-* streamlined data management
-* a tidier, more user-friendly interface
-
-# Learning Objectives {% #learning-objectives %}
-By the end of this tutorial, you'll be equipped with:
-1. The knowledge to set up a React project integrated with Appwrite.
-2. Skills to craft queries using offset pagination and understand its implications.
-3. Expertise in constructing queries with cursor pagination and recognizing its benefits.
-4. Insight into the pros and cons of different pagination techniques.
-
-# Prerequisites {% #prerequisites %}
-Before diving in, ensure you:
-
-1. Basic knowledge of JavaScript and React.
-2. Basic understanding of databases or backend systems.
-3. Have the latest version of [Node.js](https://nodejs.org/en) and [npm](https://www.npmjs.com/) installed on your computer.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-10/+page.markdoc b/src/routes/docs/tutorials/pagination/step-10/+page.markdoc
deleted file mode 100644
index c7648bf6ed..0000000000
--- a/src/routes/docs/tutorials/pagination/step-10/+page.markdoc
+++ /dev/null
@@ -1,23 +0,0 @@
----
-layout: tutorial
-title: Finalizing and further exploration
-description: Test your pagination Implementation in React with Appwrite and discover more resources to deepen your understanding.
-step: 10
----
-# Wrapping up {% #wrapping-up %}
-Pagination is a fundamental feature for applications that handle large datasets. By now, you should have a solid understanding of how to implement different pagination methods using Appwrite with React.
-# Test your pagination implementation {% #test-your-pagination-implementation %}
-Before deploying or integrating your pagination into a larger project, it's crucial to test it thoroughly:
-
- ```sh
- npm run dev -- --open --port 3000
- ```
-
- Navigate to [http://localhost:3000](http://localhost:3000) in your browser to interact with your application. Ensure that both offset and cursor pagination methods work as expected, and data loads correctly as you navigate through pages.
-
- # Dive deeper {% #dive-deeper %}
-
- To deepen your understanding and explore more advanced pagination techniques or nuances, refer to the official documentation:
-[Appwrite pagination documentation](/docs/products/databases/pagination).
-
-Remember, the choice between offset and cursor pagination will depend on your application's specific needs. Always consider the size of the dataset, user experience, and performance requirements when making your decision.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-2/+page.markdoc b/src/routes/docs/tutorials/pagination/step-2/+page.markdoc
deleted file mode 100644
index d3b486bdba..0000000000
--- a/src/routes/docs/tutorials/pagination/step-2/+page.markdoc
+++ /dev/null
@@ -1,28 +0,0 @@
----
-layout: tutorial
-title: Create app
-description: Create a React app project and integrate it with Appwrite.
-step: 2
----
-
-# Create React project {% #create-react-project %}
-
-Create a React app with the `npm create` command from [Vite](https://vitejs.dev/).
-
-```sh
-npm create vite@latest todo-app -- --template react && cd todo-app
-```
-
-# Add dependencies {% #add-dependencies %}
-
-Install the JavaScript Appwrite Web SDK.
-
-```sh
-npm install appwrite dotenv
-```
-
-You can start the development server to watch your app update in the browser as you make changes.
-
-```sh
-npm run dev -- --open --port 3000
-```
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-3/+page.markdoc b/src/routes/docs/tutorials/pagination/step-3/+page.markdoc
deleted file mode 100644
index ec10bfb47e..0000000000
--- a/src/routes/docs/tutorials/pagination/step-3/+page.markdoc
+++ /dev/null
@@ -1,60 +0,0 @@
----
-layout: tutorial
-title: Set up Appwrite
-description: Import and initialize Appwrite for your React application.
-step: 3
----
-# Create project {% #create-project %}
-
-Head to the [Appwrite Console](https://cloud.appwrite.io/console).
-
-{% only_dark %}
-
-{% /only_dark %}
-{% only_light %}
-
-{% /only_light %}
-
-If this is your first time using Appwrite, create an account and create your first project.
-
-Next, under **Add a platform**, choose **Web app**. For the **Hostname**, use "localhost".
-
-{% only_dark %}
-
-{% /only_dark %}
-{% only_light %}
-
-{% /only_light %}
-
-You can skip optional steps.
-
-# Initialize Appwrite SDK {% #init-sdk %}
-
-To use Appwrite in our React app, we need to identify our project ID. You can find this ID on the **Settings** page.
-
-{% only_dark %}
-
-{% /only_dark %}
-{% only_light %}
-
-{% /only_light %}
-
-Create a new file `src/lib/appwrite.js` to store our Appwrite-related code.
-
-{% info title="One Client() instance per an app" %}
-It's recommended to create only one instance of the `Client()` class for each app.
-{% /info %}
-
-Insert the following code, making sure to replace `` with your actual project ID.
-
-```js
-import { Client, Databases } from "appwrite";
-
-const client = new Client();
-
-client
- .setEndpoint("https://cloud.appwrite.io/v1")
- .setProject("[YOUR_PROJECT_ID]"); // Replace with your project ID
-
-export const databases = new Databases(client);
-```
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-4/+page.markdoc b/src/routes/docs/tutorials/pagination/step-4/+page.markdoc
deleted file mode 100644
index 42afc2cbc3..0000000000
--- a/src/routes/docs/tutorials/pagination/step-4/+page.markdoc
+++ /dev/null
@@ -1,57 +0,0 @@
----
-layout: tutorial
-title: Add database
-description: Add a database to your React application using Appwrite Web SDK.
-step: 4
----
-Databases are essential for dynamic applications, ensuring data is organized and accessible. They form the backbone for data storage and management. In this step, we'll integrate a database into our application using the Appwrite Web SDK, setting the stage for more advanced functionalities.
-
-# Create a database {% #create-a-database %}
-In Appwrite's console, navigate to the **Databases** page and click **Create database** and name it "development".
-
-# Create a collection {% #create-collection %}
-To create a collection, head to the **Databases** page, find to your database, and click **Create collection**. Name the collection "Todos" as it will store our todo items.
-
-When setting up your collection, make sure it has these attributes:
-
-{% table %}
-* {% width=10 %}
-* Field {% width=10 %}
-* Type {% width=10 %}
-* Required {% width=10 %}
-* Size {% width=10 %}
-* Min {% width=10 %}
-* Max {% width=10 %}
----
-*
-* taskId
-* Integer
-* Yes
-* -
-* 0
-* 500
----
-*
-* title
-* String
-* Yes
-* 248
-* -
-* -
----
-*
-* completed
-* Boolean
-* Yes
-* -
-* -
-* -
-{% /table %}
-
-# Add Permissions {% #add-permissions %}
-1. Go to the **Settings** tab.
-2. Scroll to the **Permissions** section.
-3. Add the **Any** role, ensuring you select both **CREATE** and **READ** permissions.
-{% info title="Any role" %}
- The **Any** role is used in this tutorial for demonstration purposes. Remember to protect your user's data in production applications by configuring proper [permissions](/docs/advanced/platform/permissions).
-{% /info %}
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-5/+page.markdoc b/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
deleted file mode 100644
index 7d9b06c782..0000000000
--- a/src/routes/docs/tutorials/pagination/step-5/+page.markdoc
+++ /dev/null
@@ -1,67 +0,0 @@
----
-layout: tutorial
-title: Environment setup
-description: Setup the environment for seeding the database
-step: 5
----
-# Create environment configuration file {% #create-env-file %}
-Set up the environment configuration for your project to declare project-wide variables like Appwrite project ID, database ID, and collection ID. Create a `.env` and add the declare variables.
-```sh
-APPWRITE_PROJECT_ID=[YOUR_PROJECT_ID]
-APPWRITE_DATABASE_ID=[YOUR_DATABASE_ID]
-APPWRITE_COLLECTION_ID=[YOUR_COLLECTION_ID]
-```
-This `.env` file will now act as the central place for your project's environment-specific configurations. Make sure not to sure this file publicly, especially if it contains sensitive information like API keys or database credentials.
-
-# Seeding the collection {% #seeding-the-collection %}
-We'll now create a script that populates documents from a `setup.json` file.Create a new file `./db/setup.js` in the root of your project directory for this purpose.
-
-Insert the following code, make sure that you have `[APPWRITE_DATABASE_ID]` and `[APPWRITE_COLLECTION_ID]` with your respective database and collection IDs in a `.env` file on the root.
-
-```js
-import { databases } from "../src/lib/appwrite.js";
-import { ID } from "appwrite";
-import dotenv from "dotenv";
-
-dotenv.config();
-
-const dataURL = "https://jsonplaceholder.typicode.com/users/1/todos";
-
-async function seedCollection() {
- const response = await fetch(dataURL);
- const data = await response.json();
- console.log("Starting to seed collection...");
- try {
- for (let i = 0; i < data.length; i++) {
- const item = data[i];
- let promise = await databases.createDocument(
- import.meta.env.APPWRITE_DATABASE_ID,
- import.meta.env.APPWRITE_COLLECTION_ID,
- ID.unique(),
- {
- taskId: item.id,
- title: item.title,
- completed: item.completed,
- },
- );
- console.log(`${promise.title} has been added to the collection`);
- }
- console.log("Seeding collection done!");
- } catch (error) {
- console.error(error);
- }
-}
-
-seedCollection();
-
-```
-
-# Run seed script {% #run-seed-script %}
-
-Open a terminal and run the following command:
-
-```sh
-node ./db/setup.js
-```
-
-You should see the process running in the terminal. Once it's completed, check your collection to verify that it's populated with the data.
diff --git a/src/routes/docs/tutorials/pagination/step-6/+page.markdoc b/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
deleted file mode 100644
index 07c2e378fd..0000000000
--- a/src/routes/docs/tutorials/pagination/step-6/+page.markdoc
+++ /dev/null
@@ -1,190 +0,0 @@
----
-layout: tutorial
-title: Adding todos
-description: Add todos to your React application using Appwrite.
-step: 6
----
-# Add todos contexts {% #add-todos-contexts %}
-
-We'll now display our Todos on the page. In React, [context](https://reactjs.org/docs/context.html) allows us to share data across components. We'll leverage context and create two custom hooks to manage our todos. One for each pagination method.
-
-Create a new file named `src/lib/context/todos-offset-pagination.jsx` and insert the following code:
-
-```js
-import { createContext, useContext, useEffect, useState } from "react";
-import { databases } from "../appwrite";
-import { Query } from "appwrite";
-
-const TodosContext = createContext();
-
-export function useTodos() {
- return useContext(TodosContext);
-}
-
-export function OffsetProvider(props) {
- const [todos, setTodos] = useState([]);
-
- async function init() {
- try {
- const response = await databases.listDocuments(
- import.meta.env.VITE_APPWRITE_DATABASE_ID,
- import.meta.env.VITE_APPWRITE_COLLECTION_ID,
- [Query.orderAsc("$createdAt"), Query.limit(4)],
- );
- setTodos(response.documents);
- } catch (error) {
- console.log(error);
- }
- }
-
- useEffect(() => {
- init();
- }, []);
-
- return (
-
- {props.children}
-
- );
-}
-```
-Also create a new file named `src/lib/context/todos-cursor-pagination.jsx` and insert the following code:
-
-```js
-import { createContext, useContext, useEffect, useState } from "react";
-import { databases } from "../appwrite";
-import { Query } from "appwrite";
-
-const TodosContext = createContext();
-
-export function useTodos() {
- return useContext(TodosContext);
-}
-
-export function CursorProvider(props) {
- const [todos, setTodos] = useState([]);
-
- async function init() {
- try {
- const response = await databases.listDocuments(
- import.meta.env.VITE_APPWRITE_DATABASE_ID,
- import.meta.env.VITE_APPWRITE_COLLECTION_ID,
- [Query.orderAsc("$createdAt"), Query.limit(4)],
- );
- setTodos(response.documents);
- } catch (error) {
- console.log(error);
- }
- }
-
- useEffect(() => {
- init();
- }, []);
-
- return (
-
- {props.children}
-
- );
-}
-```
-We'll expand on these contexts' functionality later.
-
-## Basic Routing {% #basic-routing %}
-The todo app will have a header that displays links to the different pagination methods so you can select each method and view the example in the container below it.
-The App function returns the Header component along with other components wrapped in OffsetProvider and CursorProvider components. These providers are used to provide data to the components that they wrap.
-
-Update `src/App.jsx` with the following code:
-
-```js
-import React from "react";
-import { OffsetProvider } from "./lib/context/todos-offset-pagination";
-import { CursorProvider } from "./lib/context/todos-cursor-pagination";
-import { TodosWithOffsetPagination } from "./pages/TodosWithOffsetPagination";
-import { TodosWithOffsetPaginationAndNumbers } from "./pages/TodosWithOffsetPaginationAndNumbers";
-import { TodosWithBasicCursorPagination } from "./pages/TodosWithBasicCursorPagination";
-import { TodosWithBidirectionalCursorPagination } from "./pages/TodosWithBidirectionalCursorPagination";
-const Header = () => (
-
- );
-}
-
-export default App;
-```
-
-### Todos page {% #todos-page %}
-
-Now, we can craft our Todos pages, which will present users with a list of todos-a small chuck of the large dataset in three different ways.
-
-Create four new files in `src/pages/` folder, name them `TodosWithOffsetPagination`, `TodosWithOffsetPaginationAndNumbers`, `TodosWithBasicCursorPagination`, and `TodosWithBidirectionalCursorPagination `. Then insert this placeholder code in each file:
-
-```js
-import { useTodos } from "../lib/context/todos";
-export function Todos() {
- const todos = useTodos();
- return (
-
-
Todos
-
- {todos.current.map((todo) => (
-
- {`Task-${todo.taskId}: `}
-
-
-
- ))}
-
-
- );
-}
-```
-we will expand these and add the UI next.
-The next steps will involve integrating offset or cursor pagination in our application.
diff --git a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
deleted file mode 100644
index 2db6c4f88e..0000000000
--- a/src/routes/docs/tutorials/pagination/step-7/+page.markdoc
+++ /dev/null
@@ -1,267 +0,0 @@
----
-layout: tutorial
-title: Implementing the offset pagination
-description: Explore offset pagination and how the react app accesses the Appwrite backend using this method.
-step: 7
----
-In this step, we'll delve into adding the offset pagination method to our app.
-
-# What is offset pagination? {% #what-is-offset-pagination %}
-Offset pagination is widely used in databases and online APIs to extract specific data subsets from a bigger whole. It requires you to tell it where to start(offset) and how many records to get(limit). In the context of software applications, offset pagination is a technique that may be employed to facilitate the content navigation by incorporating forward and back buttons on a given page.
-
-With Appwrite, offset pagination is achieved using the `Query.limit()` and `Query.offset()` methods. Here's an example using Appwrite's `listDocuments()` function to list the next four tasks:
-
-```js
-const page2 = await databases.listDocuments(
- '[TODOS_DATABASE_ID]',
- '[TODOS_COLLECTION_ID]',
- [
- Query.limit(4),
- Query.offset(4)
- ]
-);
-```
-this will retrieve the next four tasks, starting at task-5
-
-We'll explore different UI patterns for offset pagination and how to implement them in our React app.
-
-# Adding the Pagination Component {% #Adding-the-pagination-component %}
-{% tabs %}
-{% tabsitem #Basic-pagination title="Basic Pagination" %}
-
-Open the file named `src/pages/TodosWithOffsetPagination.jsx`, overwrite it with the following code:
-```js
-import { useCallback } from "react";
-import { useTodos } from "../lib/context/todos";
-
-function BasicPagination(props) {
- const { loadPrev, loadNext, hasNext, hasPrev } = props;
-
- const debounce = (func, delay) => {
- let debounceTimer;
- return (...args) => {
- clearTimeout(debounceTimer);
- debounceTimer = setTimeout(() => func(...args), delay);
- };
- };
-
- const debouncedLoadPrev = useCallback(debounce(loadPrev, 300), [loadPrev]);
-
- const debouncedLoadNext = useCallback(debounce(loadNext, 300), [loadNext]);
-
- return (
-
- 1}
- />
-
- );
-}
-export default TodosWithOffsetPagination;
-```
-This component will only have "Previous" and "Next" buttons.
-{% /tabsitem %}
-{% tabsitem #numbered-pagination title="Numbered Pagination" %}
-Open the file named `src/pages/TodosWithOffsetPaginationAndNumbers.jsx`, overwrite it with the following code:
-```js
-import { useCallback } from "react";
-import { useTodos } from "../lib/context/todos";
-
-export function TodosWithOffsetPaginationAndNumbers() {
- const todos = useTodos();
-
- return (
-
-
- );
-}
-```
-
-This component will have "Previous" and "Next" buttons along with numbered pages for direct navigation.
-{% /tabsitem %}
-{% /tabs %}
-
-# Update the todos context {% #update-the-todos-context %}
-
-Update the contents of `src/lib/context/todos.jsx` with the following code:
-
-```js
-import { createContext, useContext, useEffect, useState } from "react";
-import { databases } from "../appwrite";
-import { Query } from "appwrite";
-
-const TodosContext = createContext();
-
-export function useTodos() {
- return useContext(TodosContext);
-}
-
-export function OffsetProvider(props) {
- const [todos, setTodos] = useState([]);
- const [offset, setOffset] = useState(0);
- const [currentPage, setCurrentPage] = useState(1);
- const [totalPages, setTotalPages] = useState(0);
- const pageLimit = 4;
- const hasNext = totalPages > currentPage;
-
- async function loadNext() {
- if (hasNext) {
- setOffset(offset + pageLimit);
- }
- }
-
- async function loadPrev() {
- if (offset >= pageLimit) {
- setOffset(offset - pageLimit);
- }
- }
-
- async function loadPage(pageNumber) {
- setOffset((pageNumber - 1) * pageLimit);
- }
-
- async function init() {
- try {
- const response = await databases.listDocuments(
- import.meta.env.APPWRITE_DATABASE_ID,
- import.meta.env.APPWRITE_COLLECTION_ID,
- [
- Query.orderAsc("$createdAt"),
- Query.limit(pageLimit),
- Query.offset(offset),
- ],
- );
-
- setTodos(response.documents);
- setCurrentPage(Math.floor(offset / pageLimit) + 1);
- setTotalPages(response.total / pageLimit);
- } catch (error) {
- console.log(error);
- }
- }
- useEffect(() => {
- init();
- }, [offset]);
-
- return (
- 1,
- }}>
- {props.children}
-
- );
-}
-```
-In this updated context:
- * We're using `Query.limit()` to set the number of tasks we want to fetch per page.
- * We're using `Query.offset()` to set the starting point for fetching tasks based on the current page.
- * We've added `loadNext`, `loadPrev`, and `loadPage` function to handle pagination.
-
- Now, you can use this context in your components to display todo items and handle pagination using Appwrite's offset pagination methods.
-
-You can now choose between the two offset pagination styles based on your UI/UX preferences.
-
-In the next step, we'll transition from our current pagination method to cursor pagination method.
-
-# See it in action {% #see-it-in-action %}
-Before moving on, take a moment to run your application and see the offset pagination in action. Navigate through the tasks using the "forward" and "back" buttons and observe how the tasks are displayed based on the offset. This hands-on experience will give you a clearer understanding of how offset pagination works in a real-world scenario.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
deleted file mode 100644
index c75f43f252..0000000000
--- a/src/routes/docs/tutorials/pagination/step-8/+page.markdoc
+++ /dev/null
@@ -1,261 +0,0 @@
----
-layout: tutorial
-title: Implementing the cursor pagination
-description: Explore cursor pagination and how the React app accesses the appwrite backend using this method.
-step: 8
----
-In this step, we'll delve into adding the cursor pagination method to our app.
-
-# What is cursor pagination? {% #what-is-cursor-pagination %}
-Cursor pagination uses a unique identifier(often a timestamp, ID, or another consistently ordered field) for a specific document. This identifer acts as a pointer to the next document we want to start querying from to get the next page of data. For instance, if you have a dataset of blog posts ordered by their creation date and you want to retrieve post after a specific data, you would use that date as the cursor.
-
-With Appwrite, cursor pagination is achieved using the `Query.cursorAfter(lastId)` or the `Query.cursorBefore(firstId)` query methods. Here is an example using Appwrite's `listDocuments()` function to retrieve the next page of documents:
-
-```js
-const page2 = await databases.listDocuments(
- '[DATABASE_ID]',
- '[COLLECTION_ID]',
- [
- Query.limit(25),
- Query.cursorAfter(lastId),
- ]
-);
-```
-Cursor pagination is typically used in scenarios where the dataset is large, and the exact number of total items might not be known upfront. Here are different ways you can utilize cursor pagination:
-
-# Creating the Pagination Component {% #creating-the-pagination-component %}
-{% tabs %}
-{% tabsitem #Basic-cursor-pagination title="Basic Cursor Pagination" %}
-
-Open the file named `src/pages/TodosWithBasicCursorPagination.jsx`, overwrite the content with the following code:
-```js
-import React, { useCallback } from "react";
-import { useTodos } from "../lib/context/todos-cursor-pagination";
-
-export function TodosWithBasicCursorPagination() {
- const todos = useTodos();
-
- return (
-
-
- );
-}
-
-```
-This is the most straightforward use of cursor pagination. You have "Load More" button,which will fetch more data based on the last item's cursor.
-{% /tabsitem %}
-{% tabsitem #Bidirectional-cursor-pagination title="Bidirectional Cursor Pagination" %}
-Open the file named `src/pages/TodosWithBidirectionalCursorPagination.jsx`, overwrite the content with the following code:
-```js
-import React, { useCallback } from "react";
-import { useTodos } from "../lib/context/todos-cursor-pagination";
-
-export function TodosWithBidirectionalCursorPagination() {
- const todos = useTodos();
-
- return (
-
-
- );
-}
-```
-Bidirectional cursor pagination allows users to navigate through datasets both forwards and backwards using a cursor to maintain position, providing greater flexibility in exploring data. On the other hand, regular cursor pagination typically only supports forward navigation through the dataset, limiting the user's ability to easily access or revisit previous data entries.
-{% /tabsitem %}
-{% /tabs %}
-
-# Update todos context {% #updating-todos-context %}
-
-Update the contents of `src/lib/context/todos-cursor-pagination.jsx` with the following code:
-
-```js
-import { createContext, useContext, useEffect, useState } from "react";
-import { databases } from "../appwrite";
-import { Query } from "appwrite";
-
-const TodosContext = createContext();
-
-export function useTodos() {
- return useContext(TodosContext);
-}
-
-export function TodosProvider2(props) {
- const [todos, setTodos] = useState([]);
- const [lastId, setLastId] = useState(null);
- const [firstId, setFirstId] = useState(null);
- const [currentPage, setCurrentPage] = useState(1);
- const [totalPages, setTotalPages] = useState(0);
- const [loading, setLoading] = useState(false);
- const pageLimit = 4;
- const hasMore = totalPages > currentPage;
-
- async function loadMore() {
- if (todos.length === 0 || (hasMore && !loading)) {
- setLoading(true);
- try {
- const response = await databases.listDocuments(
- import.meta.env.APPWRITE_DATABASE_ID,
- import.meta.env.APPWRITE_COLLECTION_ID,
- [
- Query.orderAsc("$createdAt"),
- Query.limit(pageLimit),
- Query.cursorAfter(lastId),
- ],
- );
- setCurrentPage((prevPage) => prevPage + 1);
- setFirstId(response.documents[0].$id);
- setLastId(response.documents[response.documents.length - 1].$id);
- setTodos((prevTodos) => [...prevTodos, ...response.documents]);
- } catch (error) {
- console.log("Error loading more todos:", error);
- }
- setLoading(false);
- }
- }
-
- async function loadPrev() {
- if (currentPage > 1) {
- try {
- const response = await databases.listDocuments(
- import.meta.env.APPWRITE_DATABASE_ID,
- import.meta.env.APPWRITE_COLLECTION_ID,
- [
- Query.orderAsc("$createdAt"),
- Query.limit(pageLimit),
- Query.cursorBefore(firstId),
- ],
- );
- setCurrentPage(currentPage - 1);
- setFirstId(response.documents[0].$id);
- setLastId(response.documents[response.documents.length - 1].$id);
- setTodos(response.documents);
- } catch (error) {
- console.error("Error loading previous todos:", error);
- }
- }
- }
- async function init() {
- setLoading(true);
- try {
- const response = await databases.listDocuments(
- import.meta.env.APPWRITE_DATABASE_ID,
- import.meta.env.APPWRITE_COLLECTION_ID,
- [Query.orderAsc("$createdAt"), Query.limit(pageLimit)],
- );
- setFirstId(response.documents[0].$id);
- setLastId(response.documents[response.documents.length - 1].$id);
- setTodos(response.documents);
- setTotalPages(Math.ceil(response.total / pageLimit));
- } catch (error) {
- console.log("Error initializing tasks:", error);
- }
- setLoading(false);
- }
- useEffect(() => {
- init();
- }, [pageLimit]);
-
- return (
-
- {props.children}
-
- );
-}
-
-```
-In this updated context:
- * We're using `Query.cursorAfter()` is used to fetch documents that come after a specific document.
- * We're using `Query.cursorBefore()` is used to fetch documents that come before a specific document.
- * We've added `lastId` to hold the ID of the last document in the current set of todos.By using this ID with `Query.cursorAfter()`, We're essentially telling Appwrite: "Give me the next set of todos that come after this specific todo item."
- * We's added `firstId` to hold the ID of the first document in the current set of todos. By using this ID with `Query.cursorBefore()`, we're telling Appwrite: "Give me the set of todos that come before this specific todo item."
-
-Now, you can use this context in your components to display todo items and handle pagination using Appwrite's cursor pagination methods.
-
-# See it in action {% #see-it-in-action %}
-Now's the perfect time to take a moment to run your application and see cursor pagination in action. Navigate using the "Load Previous" and "Load More" buttons, you can manually navigate through tasks, showcasing the efficiency of cursor pagination.This approach ensures a smoother user experience by fetching only a subset of todo items, reducing server load and enhancing responsiveness.
diff --git a/src/routes/docs/tutorials/pagination/step-9/+page.markdoc b/src/routes/docs/tutorials/pagination/step-9/+page.markdoc
deleted file mode 100644
index e92c0213b3..0000000000
--- a/src/routes/docs/tutorials/pagination/step-9/+page.markdoc
+++ /dev/null
@@ -1,35 +0,0 @@
----
-layout: tutorial
-title: Trade-offs discussion
-description: Learn about the trade-offs between each pagination method in a React app using Appwrite backend.
-step: 9
----
-# Undstanding pagination with Appwrite and React {% #understanding-pagination-with-appwrite-and-react %}
-Pagination is a crucial feature for applications dealing with large datasets. It allows users to navigate through data in manageable chunks, ensuring that not all data is loaded at once, which can be inefficient and slow. In this section, we'll discuss the trade-offs between different pagination methods when using Appwrite and React.
-# Comparing pagination methods {% #comparing-pagination-methods %}
-
-| Feature / Aspect| Offset Pagination | Cursor Pagination |
-|----------------|-----------------------|-----------------------|
-| Simplicity | High | Medium to Low |
-| Performance | Good for initial pages, degrades with deep pagination |Consistently High |
-| Data Consistency | Can vary with data changes | More stable |
-| Scalability | Good for smaller datasets | Excellent for large datasets |
-| Implementation Complexity | Low | Medium to High |
-| Direct Page Access | Yes | No |
-
-# Choosing the right pagination method {% #choosing-the-right-pagination-method %}
-
-* Offset pagination
- * Ideal for small datasets or when the dataset doesn't change frequently.
- * Useful when direct page access is required.
- * When simplicity and quick Implementation are priorities.
-
-* Cursor pagination
- * Best suited for large datasets or real-time applications.
- * Maintains performance even with deep pagination
- * Ensures more consistent data views, especially with frequently updating datasets.
-
-# Key Takeaways {% #key-takeaways %}
- * Offset pagination is simple and allow direct page jumps but can suffer from performance issues in deep pagination.
- * Cursor pagination, while requiring more initial setup, provides consistent performance and is ideal for real-time or large datasets.
- * When using Appwrite with React, consider the dataset size, update frequency, and user navigation needs when choosing the best pagination method.
\ No newline at end of file
From ca6bd114f937c3c414d03663c7f4929096a57309 Mon Sep 17 00:00:00 2001
From: "Vincent (Wen Yu) Ge"
Date: Tue, 31 Oct 2023 14:08:13 +0000
Subject: [PATCH 12/12] Move pagination to another folder
---
.../tutorials/pagination-react/+layout.svelte | 10 +
.../tutorials/pagination-react/+layout.ts | 11 +
.../docs/tutorials/pagination-react/+page.ts | 6 +
.../pagination-react/step-1/+page.markdoc | 32 ++
.../pagination-react/step-10/+page.markdoc | 23 ++
.../pagination-react/step-2/+page.markdoc | 28 ++
.../pagination-react/step-3/+page.markdoc | 63 ++++
.../pagination-react/step-4/+page.markdoc | 57 ++++
.../pagination-react/step-5/+page.markdoc | 66 ++++
.../pagination-react/step-6/+page.markdoc | 232 ++++++++++++++
.../pagination-react/step-7/+page.markdoc | 268 ++++++++++++++++
.../pagination-react/step-8/+page.markdoc | 285 ++++++++++++++++++
.../pagination-react/step-9/+page.markdoc | 35 +++
13 files changed, 1116 insertions(+)
create mode 100644 src/routes/docs/tutorials/pagination-react/+layout.svelte
create mode 100644 src/routes/docs/tutorials/pagination-react/+layout.ts
create mode 100644 src/routes/docs/tutorials/pagination-react/+page.ts
create mode 100644 src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc
create mode 100644 src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc
diff --git a/src/routes/docs/tutorials/pagination-react/+layout.svelte b/src/routes/docs/tutorials/pagination-react/+layout.svelte
new file mode 100644
index 0000000000..fb9fb3980f
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/+layout.svelte
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/routes/docs/tutorials/pagination-react/+layout.ts b/src/routes/docs/tutorials/pagination-react/+layout.ts
new file mode 100644
index 0000000000..562b11506f
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/+layout.ts
@@ -0,0 +1,11 @@
+import type { LayoutLoad } from './$types';
+
+export const load: LayoutLoad = ({ url }) => {
+ const tutorials = import.meta.glob('./**/*.markdoc', {
+ eager: true
+ });
+ return {
+ tutorials,
+ pathname: url.pathname
+ };
+};
diff --git a/src/routes/docs/tutorials/pagination-react/+page.ts b/src/routes/docs/tutorials/pagination-react/+page.ts
new file mode 100644
index 0000000000..388792ab49
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/+page.ts
@@ -0,0 +1,6 @@
+import { redirect } from '@sveltejs/kit';
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async () => {
+ throw redirect(303, '/docs/tutorials/pagination-react/step-1');
+};
diff --git a/src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc
new file mode 100644
index 0000000000..f5557af934
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-1/+page.markdoc
@@ -0,0 +1,32 @@
+---
+layout: tutorial
+title: Pagination deep-dive with React
+description: Dive deep into integrating pagination in a React application using the Appwrite backend
+step: 1
+difficulty: intermediate
+readtime: 30
+---
+
+ In this tutorial, you'll learn how to seamlessly integrate Appwrite's pagination methods into a React application.
+ Proper pagination ensures efficient data display, enhance user experience, and provide a smooth interface for navigating large datasets.
+
+ While this is a React example, the same priciples should apply to any other framework or platform.
+
+# Why pagination? {% #why-pagination %}
+Pagination is critical in web development, especially when working with large datasets.
+It helps users explore and process data without feeling overwhelmed by breaking it down into smaller parts.
+With large data sets, it also lets you improve performance and lower resource use by fetching a small "page" of data to be processed at a time.
+
+# Concepts {% #concepts %}
+1. What is pagination?
+1. Building different pagination UIs in React.
+1. Offset pagination.
+1. Cursor pagination.
+1. Trade-offs between offset and cursor pagination.
+
+# Prerequisites {% #prerequisites %}
+Before diving in, ensure you:
+
+1. Basic knowledge of JavaScript and React.
+2. Basic understanding of Appwrite Databases.
+3. Have the latest version of [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc
new file mode 100644
index 0000000000..c7648bf6ed
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-10/+page.markdoc
@@ -0,0 +1,23 @@
+---
+layout: tutorial
+title: Finalizing and further exploration
+description: Test your pagination Implementation in React with Appwrite and discover more resources to deepen your understanding.
+step: 10
+---
+# Wrapping up {% #wrapping-up %}
+Pagination is a fundamental feature for applications that handle large datasets. By now, you should have a solid understanding of how to implement different pagination methods using Appwrite with React.
+# Test your pagination implementation {% #test-your-pagination-implementation %}
+Before deploying or integrating your pagination into a larger project, it's crucial to test it thoroughly:
+
+ ```sh
+ npm run dev -- --open --port 3000
+ ```
+
+ Navigate to [http://localhost:3000](http://localhost:3000) in your browser to interact with your application. Ensure that both offset and cursor pagination methods work as expected, and data loads correctly as you navigate through pages.
+
+ # Dive deeper {% #dive-deeper %}
+
+ To deepen your understanding and explore more advanced pagination techniques or nuances, refer to the official documentation:
+[Appwrite pagination documentation](/docs/products/databases/pagination).
+
+Remember, the choice between offset and cursor pagination will depend on your application's specific needs. Always consider the size of the dataset, user experience, and performance requirements when making your decision.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc
new file mode 100644
index 0000000000..d3b486bdba
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-2/+page.markdoc
@@ -0,0 +1,28 @@
+---
+layout: tutorial
+title: Create app
+description: Create a React app project and integrate it with Appwrite.
+step: 2
+---
+
+# Create React project {% #create-react-project %}
+
+Create a React app with the `npm create` command from [Vite](https://vitejs.dev/).
+
+```sh
+npm create vite@latest todo-app -- --template react && cd todo-app
+```
+
+# Add dependencies {% #add-dependencies %}
+
+Install the JavaScript Appwrite Web SDK.
+
+```sh
+npm install appwrite dotenv
+```
+
+You can start the development server to watch your app update in the browser as you make changes.
+
+```sh
+npm run dev -- --open --port 3000
+```
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc
new file mode 100644
index 0000000000..7436296678
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-3/+page.markdoc
@@ -0,0 +1,63 @@
+---
+layout: tutorial
+title: Set up Appwrite
+description: Import and initialize Appwrite for your React application.
+step: 3
+---
+# Create project {% #create-project %}
+
+Head to the [Appwrite Console](https://cloud.appwrite.io/console).
+
+{% only_dark %}
+
+{% /only_dark %}
+{% only_light %}
+
+{% /only_light %}
+
+If this is your first time using Appwrite, create an account and create your first project.
+
+Next, under **Add a platform**, choose **Web app**. For the **Hostname**, use "localhost".
+
+{% only_dark %}
+
+{% /only_dark %}
+{% only_light %}
+
+{% /only_light %}
+
+You can skip optional steps.
+
+# Initialize Appwrite SDK {% #init-sdk %}
+
+To use Appwrite in our React app, we need to identify our project ID. You can find this ID on the **Settings** page.
+
+{% only_dark %}
+
+{% /only_dark %}
+{% only_light %}
+
+{% /only_light %}
+
+Create a new file `src/lib/appwrite.js` to store our Appwrite-related code.
+
+```js
+import { Client, Databases } from "appwrite";
+
+const client = new Client();
+
+client
+ .setEndpoint("https://cloud.appwrite.io/v1")
+ .setProject("import.meta.env.VITE_APPWRITE_PROJECT_ID");
+
+export const databases = new Databases(client);
+```
+
+# Create environment file {% #create-env-file %}
+Set up the environment configuration for your project to declare project-wide variables like Appwrite project ID, database ID, and collection ID. Create a `.env` and add the declare variables.
+```sh
+VITE_APPWRITE_PROJECT_ID=
+VITE_APPWRITE_DATABASE_ID=
+VITE_APPWRITE_COLLECTION_ID=
+```
+This `.env` file will now act as the central place for your project's environment-specific configurations. Make sure not to sure this file publicly, especially if it contains sensitive information like API keys or database credentials.
diff --git a/src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc
new file mode 100644
index 0000000000..d7f5f8f485
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-4/+page.markdoc
@@ -0,0 +1,57 @@
+---
+layout: tutorial
+title: Add database
+description: Add a database to your React application using Appwrite Web SDK.
+step: 4
+---
+Before we can talk about pagination, we need to create some mock data to paginate.
+
+# Create a database {% #create-a-database %}
+In Appwrite's console, navigate to the **Databases** page and click **Create database** and name it "Development".
+
+# Create a collection {% #create-collection %}
+To create a collection, head to the **Databases** page, find your database, and click **Create collection**. Name the collection "Todos".
+
+Create the following attributes in your collection.
+
+{% table %}
+* {% width=10 %}
+* Field {% width=10 %}
+* Type {% width=10 %}
+* Required {% width=10 %}
+* Size {% width=10 %}
+* Min {% width=10 %}
+* Max {% width=10 %}
+---
+*
+* taskId
+* Integer
+* Yes
+* -
+* 0
+* 500
+---
+*
+* title
+* String
+* Yes
+* 248
+* -
+* -
+---
+*
+* completed
+* Boolean
+* Yes
+* -
+* -
+* -
+{% /table %}
+
+# Add Permissions {% #add-permissions %}
+1. Go to the **Settings** tab.
+2. Scroll to the **Permissions** section.
+3. Add the **Any** role, ensuring you select both **CREATE** and **READ** permissions.
+{% info title="Any role" %}
+ The **Any** role is used in this tutorial for demonstration purposes. Remember to protect your user's data in production applications by configuring proper [permissions](/docs/advanced/platform/permissions).
+{% /info %}
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc
new file mode 100644
index 0000000000..7923325028
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-5/+page.markdoc
@@ -0,0 +1,66 @@
+---
+layout: tutorial
+title: Environment setup
+description: Setup the environment for seeding the database
+step: 5
+---
+
+# Seeding the collection {% #seeding-the-collection %}
+We'll now create a script that populates documents from a `setup.json` file.Create a new file `./db/setup.js` in the root of your project directory for this purpose.
+
+Insert the following code, make sure that you have `[APPWRITE_DATABASE_ID]` and `[APPWRITE_COLLECTION_ID]` with your respective database and collection IDs in a `.env` file on the root.
+
+```js
+import { Client, Databases, ID } from "appwrite";
+
+import dotenv from "dotenv";
+dotenv.config();
+
+const client = new Client();
+
+client
+ .setEndpoint("https://cloud.appwrite.io/v1")
+ .setProject(process.env.VITE_APPWRITE_PROJECT_ID); // Replace with your project ID
+
+export const databases = new Databases(client);
+
+
+const dataURL = "https://jsonplaceholder.typicode.com/users/1/todos";
+
+async function seedCollection() {
+ const response = await fetch(dataURL);
+ const data = await response.json();
+ console.log("Starting to seed collection...");
+ try {
+ for (let i = 0; i < data.length; i++) {
+ const item = data[i];
+ let promise = await databases.createDocument(
+ process.env.VITE_APPWRITE_DATABASE_ID,
+ process.env.VITE_APPWRITE_COLLECTION_ID,
+ ID.unique(),
+ {
+ taskId: item.id,
+ title: item.title,
+ completed: item.completed,
+ },
+ );
+ console.log(`${promise.title} has been added to the collection`);
+ }
+ console.log("Seeding collection done!");
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+seedCollection();
+```
+
+# Run seed script {% #run-seed-script %}
+
+Open a terminal and run the following command:
+
+```sh
+node ./db/setup.js
+```
+
+You should see the process running in the terminal. Once it's completed, check your collection to verify that it's populated with the data.
diff --git a/src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc
new file mode 100644
index 0000000000..d4adfc8a48
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-6/+page.markdoc
@@ -0,0 +1,232 @@
+---
+layout: tutorial
+title: Adding todos
+description: Add todos to your React application using Appwrite.
+step: 6
+---
+# Add todos contexts {% #add-todos-contexts %}
+
+We'll now display our Todos in our app. In React, [context](https://reactjs.org/docs/context.html) allows us to share data across components.
+We'll leverage context and create two custom contexts to manage our todos. One for each pagination method.
+
+Create a new file named `src/lib/context/OffsetProvider.jsx` and insert the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function OffsetProvider(props) {
+ const [todos, setTodos] = useState([]);
+
+ async function init() {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [Query.orderAsc("$createdAt"), Query.limit(4)],
+ );
+ setTodos(response.documents);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ useEffect(() => {
+ init();
+ }, []);
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+Also create a new file named `src/lib/context/CursorProvider.jsx` and insert the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function CursorProvider(props) {
+ const [todos, setTodos] = useState([]);
+
+ async function init() {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [Query.orderAsc("$createdAt"), Query.limit(4)],
+ );
+ setTodos(response.documents);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ useEffect(() => {
+ init();
+ }, []);
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+We'll expand on these contexts' functionality later.
+
+## Basic Routing {% #basic-routing %}
+The todo app will have a header that displays links to the different pagination methods so you can select each method and view the example in the container below it.
+The `App` fragment returns the `Header` component along with other components wrapped in `OffsetProvider` and `CursorProvider` components.
+These providers are used to provide data to the components that they wrap.
+
+Update `src/App.jsx` with the following code:
+
+```js
+import React from "react";
+import { OffsetProvider } from "./lib/context/OffsetProvider";
+import { CursorProvider } from "./lib/context/CursorProvider";
+import { TodosWithOffsetPagination } from "./pages/TodosWithOffsetPagination";
+import { TodosWithOffsetPaginationAndNumbers } from "./pages/TodosWithOffsetPaginationAndNumbers";
+import { TodosWithBasicCursorPagination } from "./pages/TodosWithBasicCursorPagination";
+import { TodosWithBidirectionalCursorPagination } from "./pages/TodosWithBidirectionalCursorPagination";
+const Header = () => (
+
+ );
+}
+
+export default App;
+```
+
+### Todos page {% #todos-page %}
+
+Now, we can craft our Todos pages, which will present users with a list of todos-a small chuck of the large dataset in three different ways.
+
+Create four new files in `src/pages/` folder, name them `TodosWithOffsetPagination`, `TodosWithOffsetPaginationAndNumbers`, `TodosWithBasicCursorPagination`, and `TodosWithBidirectionalCursorPagination`. Then insert this placeholder code in each file:
+
+{% tabs %}
+{% tabsitem #todos-with-offset-pagination title="TodosWithOffsetPagination" %}
+```js
+import { useTodos } from "../lib/context/OffsetProvider";
+export function TodosWithOffsetPagination() {
+ const todos = useTodos();
+ return (
+
+
+
+ );
+}
+```
+{% /tabsitem %}
+{% /tabs %}
+
+We will expand these and add UI next.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc
new file mode 100644
index 0000000000..aaea05250a
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-7/+page.markdoc
@@ -0,0 +1,268 @@
+---
+layout: tutorial
+title: Implementing the offset pagination
+description: Explore offset pagination and how the react app accesses the Appwrite backend using this method.
+step: 7
+---
+In this step, we'll delve into adding the offset pagination method to our app.
+
+# What is offset pagination? {% #what-is-offset-pagination %}
+Offset pagination is widely used in databases and online APIs to extract specific data subsets from a bigger whole. It requires you to tell it where to start (offset) and how many records to get (limit).
+In the context of software applications, offset pagination is a technique that may be employed to facilitate the content navigation by incorporating forward and back buttons on a given page.
+
+With Appwrite, offset pagination is achieved using the `Query.limit()` and `Query.offset()` methods.
+Here's an example using Appwrite's `listDocuments()` function to list the next four tasks:
+
+```js
+const page2 = await databases.listDocuments(
+ '[TODOS_DATABASE_ID]',
+ '[TODOS_COLLECTION_ID]',
+ [
+ Query.limit(4),
+ Query.offset(4)
+ ]
+);
+```
+this will retrieve the next four tasks, starting at task-5
+
+We'll explore different UI patterns for offset pagination and how to implement them in our React app.
+
+# Adding the Pagination Component {% #Adding-the-pagination-component %}
+{% tabs %}
+{% tabsitem #Basic-pagination title="Basic Pagination" %}
+
+Open the file named `src/pages/TodosWithOffsetPagination.jsx`, overwrite it with the following code:
+```js
+import { useCallback } from "react";
+import { useTodos } from "../lib/context/OffsetProvider";
+
+export function TodosWithOffsetPagination() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+```
+This component will only have "Previous" and "Next" buttons.
+{% /tabsitem %}
+{% tabsitem #numbered-pagination title="Numbered Pagination" %}
+Open the file named `src/pages/TodosWithOffsetPaginationAndNumbers.jsx`, overwrite it with the following code:
+```js
+import { useCallback } from "react";
+import { useTodos } from "../lib/context/OffsetProvider";
+
+export function TodosWithOffsetPaginationAndNumbers() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+```
+
+This component will have "Previous" and "Next" buttons along with numbered pages for direct navigation.
+{% /tabsitem %}
+{% /tabs %}
+
+# Update OffsetProvider context {% #update-offsetprovider-context %}
+
+Update the contents of `src/lib/context/OffsetProvider.jsx` with the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function OffsetProvider(props) {
+ const [todos, setTodos] = useState([]);
+ const [offset, setOffset] = useState(0);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(0);
+ const pageLimit = 4;
+ const hasNext = totalPages > currentPage;
+
+ async function loadNext() {
+ if (hasNext) {
+ setOffset(offset + pageLimit);
+ }
+ }
+
+ async function loadPrev() {
+ if (offset >= pageLimit) {
+ setOffset(offset - pageLimit);
+ }
+ }
+
+ async function loadPage(pageNumber) {
+ setOffset((pageNumber - 1) * pageLimit);
+ }
+
+ async function init() {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.offset(offset),
+ ],
+ );
+
+ setTodos(response.documents);
+ setCurrentPage(Math.floor(offset / pageLimit) + 1);
+ setTotalPages(response.total / pageLimit);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ useEffect(() => {
+ init();
+ }, [offset]);
+
+ return (
+ 1,
+ }}>
+ {props.children}
+
+ );
+}
+```
+In this updated context:
+ * We're using `Query.limit()` to set the number of tasks we want to fetch per page.
+ * We're using `Query.offset()` to set the starting point for fetching tasks based on the current page.
+ * We've added `loadNext`, `loadPrev`, and `loadPage` function to handle pagination.
+
+ Now, you can use this context in your components to display todo items and handle pagination using Appwrite's offset pagination methods.
+
+You can now choose between the two offset pagination styles based on your UI/UX preferences.
+
+In the next step, we'll transition from our current pagination method to cursor pagination method.
+
+# See it in action {% #see-it-in-action %}
+Before moving on, take a moment to run your application and see the offset pagination in action. Navigate through the tasks using the "forward" and "back" buttons and observe how the tasks are displayed based on the offset. This hands-on experience will give you a clearer understanding of how offset pagination works in a real-world scenario.
\ No newline at end of file
diff --git a/src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc
new file mode 100644
index 0000000000..d46d52e0c8
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-8/+page.markdoc
@@ -0,0 +1,285 @@
+---
+layout: tutorial
+title: Implementing the cursor pagination
+description: Explore cursor pagination and how the React app accesses the appwrite backend using this method.
+step: 8
+---
+In this step, we'll delve into adding the cursor pagination method to our app.
+
+# What is cursor pagination? {% #what-is-cursor-pagination %}
+Cursor pagination uses a unique identifier(often a timestamp, ID, or another consistently ordered field) for a specific document. This identifer acts as a pointer to the next document we want to start querying from to get the next page of data. For instance, if you have a dataset of blog posts ordered by their creation date and you want to retrieve post after a specific data, you would use that date as the cursor.
+
+With Appwrite, cursor pagination is achieved using the `Query.cursorAfter(lastId)` or the `Query.cursorBefore(firstId)` query methods. Here is an example using Appwrite's `listDocuments()` function to retrieve the next page of documents:
+
+```js
+const page2 = await databases.listDocuments(
+ '[DATABASE_ID]',
+ '[COLLECTION_ID]',
+ [
+ Query.limit(25),
+ Query.cursorAfter(lastId),
+ ]
+);
+```
+Cursor pagination is typically used in scenarios where the dataset is large, and the exact number of total items might not be known upfront. Here are different ways you can utilize cursor pagination:
+
+# Creating the Pagination Component {% #creating-the-pagination-component %}
+{% tabs %}
+{% tabsitem #Basic-cursor-pagination title="Basic Cursor Pagination" %}
+
+Open the file named `src/pages/TodosWithBasicCursorPagination.jsx`, overwrite the content with the following code:
+```js
+import React, { useCallback } from "react";
+import { useTodos } from "../lib/context/CursorProvider";
+
+export function TodosWithBasicCursorPagination() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+
+```
+This is the most straightforward use of cursor pagination. You have "Load More" button,which will fetch more data based on the last item's cursor.
+{% /tabsitem %}
+{% tabsitem #Bidirectional-cursor-pagination title="Bidirectional Cursor Pagination" %}
+Open the file named `src/pages/TodosWithBidirectionalCursorPagination.jsx`, overwrite the content with the following code:
+```js
+import React, { useCallback } from "react";
+import { useTodos } from "../lib/context/CursorProvider";
+
+export function TodosWithBidirectionalCursorPagination() {
+ const todos = useTodos();
+
+ return (
+
+
+ );
+}
+```
+Bidirectional cursor pagination allows users to navigate through datasets both forwards and backwards using a cursor to maintain position, providing greater flexibility in exploring data. On the other hand, regular cursor pagination typically only supports forward navigation through the dataset, limiting the user's ability to easily access or revisit previous data entries.
+{% /tabsitem %}
+{% /tabs %}
+
+# Update CursorProvider context {% #updating-cursorprovider-context %}
+
+Update the contents of `src/lib/context/CursorProvider.jsx` with the following code:
+
+```js
+import { createContext, useContext, useEffect, useState } from "react";
+import { databases } from "../appwrite";
+import { Query } from "appwrite";
+
+const TodosContext = createContext();
+
+export function useTodos() {
+ return useContext(TodosContext);
+}
+
+export function CursorProvider(props) {
+ const [todos, setTodos] = useState([]);
+ const [lastId, setLastId] = useState(null);
+ const [firstId, setFirstId] = useState(null);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const pageLimit = 4;
+ const hasMore = totalPages > currentPage;
+
+ async function loadMore() {
+ if (todos.length === 0 || (hasMore && !loading)) {
+ setLoading(true);
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.cursorAfter(lastId),
+ ],
+ );
+ setCurrentPage((prevPage) => prevPage + 1);
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos((prevTodos) => [...prevTodos, ...response.documents]);
+ } catch (error) {
+ console.log("Error loading more todos:", error);
+ }
+ setLoading(false);
+ }
+ }
+
+ async function loadNext() {
+ if (todos.length === 0 || (hasMore && !loading)) {
+ setLoading(true);
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.cursorAfter(lastId),
+ ],
+ );
+ setCurrentPage((prevPage) => prevPage + 1);
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos(response.documents);
+ } catch (error) {
+ console.log("Error loading more todos:", error);
+ }
+ setLoading(false);
+ }
+ }
+
+ async function loadPrev() {
+ if (currentPage > 1) {
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [
+ Query.orderAsc("$createdAt"),
+ Query.limit(pageLimit),
+ Query.cursorBefore(firstId),
+ ],
+ );
+ setCurrentPage(currentPage - 1);
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos(response.documents);
+ } catch (error) {
+ console.error("Error loading previous todos:", error);
+ }
+ }
+ }
+ async function init() {
+ setLoading(true);
+ try {
+ const response = await databases.listDocuments(
+ import.meta.env.VITE_APPWRITE_DATABASE_ID,
+ import.meta.env.VITE_APPWRITE_COLLECTION_ID,
+ [Query.orderAsc("$createdAt"), Query.limit(pageLimit)],
+ );
+ setFirstId(response.documents[0].$id);
+ setLastId(response.documents[response.documents.length - 1].$id);
+ setTodos(response.documents);
+ setTotalPages(Math.ceil(response.total / pageLimit));
+ } catch (error) {
+ console.log("Error initializing tasks:", error);
+ }
+ setLoading(false);
+ }
+ useEffect(() => {
+ init();
+ }, [pageLimit]);
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+In this updated context:
+ * We're using `Query.cursorAfter()` is used to fetch documents that come after a specific document.
+ * We're using `Query.cursorBefore()` is used to fetch documents that come before a specific document.
+ * We've added `lastId` to hold the ID of the last document in the current set of todos.By using this ID with `Query.cursorAfter()`, We're essentially telling Appwrite: "Give me the next set of todos that come after this specific todo item."
+ * We's added `firstId` to hold the ID of the first document in the current set of todos. By using this ID with `Query.cursorBefore()`, we're telling Appwrite: "Give me the set of todos that come before this specific todo item."
+
+Now, you can use this context in your components to display todo items and handle pagination using Appwrite's cursor pagination methods.
+
+# See it in action {% #see-it-in-action %}
+Now's the perfect time to take a moment to run your application and see cursor pagination in action. Navigate using the "Load Previous" and "Load More" buttons, you can manually navigate through tasks, showcasing the efficiency of cursor pagination.This approach ensures a smoother user experience by fetching only a subset of todo items, reducing server load and enhancing responsiveness.
diff --git a/src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc b/src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc
new file mode 100644
index 0000000000..e92c0213b3
--- /dev/null
+++ b/src/routes/docs/tutorials/pagination-react/step-9/+page.markdoc
@@ -0,0 +1,35 @@
+---
+layout: tutorial
+title: Trade-offs discussion
+description: Learn about the trade-offs between each pagination method in a React app using Appwrite backend.
+step: 9
+---
+# Undstanding pagination with Appwrite and React {% #understanding-pagination-with-appwrite-and-react %}
+Pagination is a crucial feature for applications dealing with large datasets. It allows users to navigate through data in manageable chunks, ensuring that not all data is loaded at once, which can be inefficient and slow. In this section, we'll discuss the trade-offs between different pagination methods when using Appwrite and React.
+# Comparing pagination methods {% #comparing-pagination-methods %}
+
+| Feature / Aspect| Offset Pagination | Cursor Pagination |
+|----------------|-----------------------|-----------------------|
+| Simplicity | High | Medium to Low |
+| Performance | Good for initial pages, degrades with deep pagination |Consistently High |
+| Data Consistency | Can vary with data changes | More stable |
+| Scalability | Good for smaller datasets | Excellent for large datasets |
+| Implementation Complexity | Low | Medium to High |
+| Direct Page Access | Yes | No |
+
+# Choosing the right pagination method {% #choosing-the-right-pagination-method %}
+
+* Offset pagination
+ * Ideal for small datasets or when the dataset doesn't change frequently.
+ * Useful when direct page access is required.
+ * When simplicity and quick Implementation are priorities.
+
+* Cursor pagination
+ * Best suited for large datasets or real-time applications.
+ * Maintains performance even with deep pagination
+ * Ensures more consistent data views, especially with frequently updating datasets.
+
+# Key Takeaways {% #key-takeaways %}
+ * Offset pagination is simple and allow direct page jumps but can suffer from performance issues in deep pagination.
+ * Cursor pagination, while requiring more initial setup, provides consistent performance and is ideal for real-time or large datasets.
+ * When using Appwrite with React, consider the dataset size, update frequency, and user navigation needs when choosing the best pagination method.
\ No newline at end of file