From 4a1f23989ad919ed85bc0970732d18142576c6d7 Mon Sep 17 00:00:00 2001 From: sansx <646924078@qq.com> Date: Sat, 13 Apr 2024 00:24:40 +0800 Subject: [PATCH 1/2] update --- .../backend/redis_todos/mod.rs | 1 + .../backend/redis_todos/todos.rs | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 create-rust-app_cli/template-plugin-redis/backend/redis_todos/mod.rs create mode 100644 create-rust-app_cli/template-plugin-redis/backend/redis_todos/todos.rs diff --git a/create-rust-app_cli/template-plugin-redis/backend/redis_todos/mod.rs b/create-rust-app_cli/template-plugin-redis/backend/redis_todos/mod.rs new file mode 100644 index 00000000..015a6a2b --- /dev/null +++ b/create-rust-app_cli/template-plugin-redis/backend/redis_todos/mod.rs @@ -0,0 +1 @@ +pub mod todos; diff --git a/create-rust-app_cli/template-plugin-redis/backend/redis_todos/todos.rs b/create-rust-app_cli/template-plugin-redis/backend/redis_todos/todos.rs new file mode 100644 index 00000000..9e228acc --- /dev/null +++ b/create-rust-app_cli/template-plugin-redis/backend/redis_todos/todos.rs @@ -0,0 +1,59 @@ +// use redis::Commands; +// use redis::Connection; + +use redis::Client; +use redis::Commands; +use redis::Connection; +use redis::JsonCommands; +use redis::RedisError; +use redis::RedisResult; +use redis::ToRedisArgs; + +use redis_macros::{FromRedisValue, ToRedisArgs}; +use serde::{Deserialize, Serialize}; + +use std::cell::RefCell; + +use serde_json::json; + +#[derive(Debug, Serialize, Deserialize, Clone, FromRedisValue, ToRedisArgs)] +pub struct Todo { + pub text: String, + pub created_at: chrono::DateTime, + pub updated_at: chrono::DateTime, +} + +pub struct RedisDB { + pub conn: RefCell, +} + +impl RedisDB { + pub fn init() -> Result { + // "redis://127.0.0.1:6379" + + let client = redis::Client::open(Self::connection_url())?; + Ok(RedisDB { + conn: RefCell::new(client.get_connection()?), + }) + } + + pub fn connection_url() -> String { + std::env::var("REDIS_URL").expect("REDIS_URL environment variable expected.") + } + + pub fn create(&mut self, item: &Todo) -> Result { + let mut con = self.conn.borrow_mut(); + // throw away the result, just make sure it does not fail + println!("1111{:?}", &json!(item).to_string()); + let res = con.lpush("todo_list", &json!(item).to_string())?; + + Ok(res) + } + + pub fn getList(&mut self) -> Result, redis::RedisError> { + let mut con = self.conn.borrow_mut(); + // throw away the result, just make sure it does not fail + let res_list: Vec = con.lrange("todo_list", 0, 10)?; + Ok(res_list) + } +} From ec0bbd914a0950e16f65be48dc671363a964ebf7 Mon Sep 17 00:00:00 2001 From: sansx <646924078@qq.com> Date: Sat, 13 Apr 2024 00:26:48 +0800 Subject: [PATCH 2/2] update frontend --- .../frontend/containers/Home.tsx | 39 ++++ .../frontend/containers/Todo.tsx | 205 ++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 create-rust-app_cli/template-plugin-redis/frontend/containers/Home.tsx create mode 100644 create-rust-app_cli/template-plugin-redis/frontend/containers/Todo.tsx diff --git a/create-rust-app_cli/template-plugin-redis/frontend/containers/Home.tsx b/create-rust-app_cli/template-plugin-redis/frontend/containers/Home.tsx new file mode 100644 index 00000000..9957126c --- /dev/null +++ b/create-rust-app_cli/template-plugin-redis/frontend/containers/Home.tsx @@ -0,0 +1,39 @@ +import React from 'react' +import reactLogo from '../images/logo.svg' +import rustLogo from '../images/logo2.svg' +import plus from '../images/plus.svg' + +export const Home = () => { + return ( +
+
+ rust-logo + plus + react-logo +
+

+ Edit frontend/src/App.tsx and save to reload. +

+ + +
+ ) +} diff --git a/create-rust-app_cli/template-plugin-redis/frontend/containers/Todo.tsx b/create-rust-app_cli/template-plugin-redis/frontend/containers/Todo.tsx new file mode 100644 index 00000000..4e933079 --- /dev/null +++ b/create-rust-app_cli/template-plugin-redis/frontend/containers/Todo.tsx @@ -0,0 +1,205 @@ +import React, { useEffect, useState } from "react"; + +const TodoAPI = { + get: async (page: number, size: number) => + await (await fetch(`/api/todos?page=${page}&page_size=${size}`)).json(), + + getRedis: async (page: number = 1, size: number = 10) => + await ( + await fetch(`/api/redis_todos?page=${page}&page_size=${size}`) + ).json(), + create: async (todo: string) => + await ( + await fetch("/api/todos", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ text: todo }), + }) + ).json(), + createRedis: async (todo: string) => + await ( + await fetch("/api/redis_todos", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + text: todo, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + }), + }) + ).json(), + delete: async (id: number) => + await fetch(`/api/todos/${id}`, { method: "DELETE" }), + update: async (id: number, todo: string) => + await fetch(`/api/todos/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ text: todo }), + }), +}; + +export const Todos = () => { + const [text, setText] = useState(""); + const [selectedTodo, editTodo] = useState(null); + const [todos, setTodos] = useState>(); + const [createdTodo, setCreatedTodo] = useState(); + const pageSize = 5; + const [page, setPage] = useState(0); + const [numPages, setPages] = useState(1); + const [processing, setProcessing] = useState(false); + + const createTodo = async (todo: string) => { + setProcessing(true); + let createdTodo = await TodoAPI.createRedis(todo); + let todos = await TodoAPI.get(page, pageSize); + await TodoAPI.getRedis(page, pageSize); + setTodos(todos); + setCreatedTodo(createdTodo); + setText(""); + setProcessing(false); + }; + + const updateTodo = async (todo: Todo) => { + setProcessing(true); + await TodoAPI.update(todo.id, text); + setTodos(await TodoAPI.get(page, pageSize)); + setText(""); + editTodo(null); + setProcessing(false); + }; + + const deleteTodo = async (todo: Todo) => { + setProcessing(true); + await TodoAPI.delete(todo.id); + setTodos(await TodoAPI.get(page, pageSize)); + setProcessing(false); + }; + + useEffect(() => { + TodoAPI.getRedis(); + }, []); + + useEffect(() => { + setText(selectedTodo?.text || ""); + }, [selectedTodo]); + + // fetch on page change + useEffect(() => { + setProcessing(true); + TodoAPI.get(page, pageSize).then((todos) => { + setTodos(todos); + setProcessing(false); + }); + }, [page]); + + // update total number of pages + useEffect(() => { + if (todos) setPages(todos?.num_pages); + }, [todos]); + + useEffect(() => { + editTodo(null); + if (page < 0) setPage(0); + if (numPages != 0 && page >= numPages) setPage(numPages - 1); + }, [page, numPages]); + + useEffect(() => { + // go to the latest page when a new todo is created + setPage(numPages - 1); + setCreatedTodo(undefined); + }, [createdTodo]); + + return ( +
+

Todos

+ {(!todos || todos.total_items === 0) && "No todos, create one!"} + {todos?.items.map((todo) => + todo.id === selectedTodo?.id ? ( +
+
+ setText(e.target.value)} + /> + + +
+
+ ) : ( +
+
+ #{todo.id} {todo.text} +
+ +
+ ) + )} + {selectedTodo === null && ( +
+
+ setText(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + createTodo(text); + } + }} + /> + +
+
+ )} +
+
+ + + Page {page + 1} of {numPages} + + +
+
+
+ ); +};