Add Random resource retrieval and navigation.

This commit is contained in:
2025-06-08 17:49:49 +08:00
parent b0680fa94f
commit 99a2d328f1
9 changed files with 132 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ import UserPage from "./pages/user_page.tsx";
import EditResourcePage from "./pages/edit_resource_page.tsx"; import EditResourcePage from "./pages/edit_resource_page.tsx";
import AboutPage from "./pages/about_page.tsx"; import AboutPage from "./pages/about_page.tsx";
import TagsPage from "./pages/tags_page.tsx"; import TagsPage from "./pages/tags_page.tsx";
import RandomPage from "./pages/random_page.tsx";
export default function App() { export default function App() {
return ( return (
@@ -30,6 +31,7 @@ export default function App() {
<Route path={"/resource/edit/:rid"} element={<EditResourcePage />} /> <Route path={"/resource/edit/:rid"} element={<EditResourcePage />} />
<Route path={"/about"} element={<AboutPage />} /> <Route path={"/about"} element={<AboutPage />} />
<Route path={"/tags"} element={<TagsPage />} /> <Route path={"/tags"} element={<TagsPage />} />
<Route path={"/random"} element={<RandomPage />} />
</Route> </Route>
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>

View File

@@ -103,6 +103,17 @@ export default function Navigator() {
{"Github"} {"Github"}
</a> </a>
</li> </li>
<li
onClick={() => {
const menu = document.getElementById(
"navi_menu",
) as HTMLElement;
menu.blur();
navigate("/random");
}}
>
<a>{t("Random")}</a>
</li>
<li <li
onClick={() => { onClick={() => {
const menu = document.getElementById( const menu = document.getElementById(
@@ -143,6 +154,13 @@ export default function Navigator() {
> >
<a>{t("Tags")}</a> <a>{t("Tags")}</a>
</li> </li>
<li
onClick={() => {
navigate("/random");
}}
>
<a>{t("Random")}</a>
</li>
<li <li
onClick={() => { onClick={() => {
navigate("/about"); navigate("/about");

View File

@@ -211,6 +211,7 @@ export const i18nData = {
"Ocean Breeze": "Ocean Breeze", "Ocean Breeze": "Ocean Breeze",
"Mint Leaf": "Mint Leaf", "Mint Leaf": "Mint Leaf",
"Golden Glow": "Golden Glow", "Golden Glow": "Golden Glow",
"Random": "Random",
}, },
}, },
"zh-CN": { "zh-CN": {
@@ -414,6 +415,8 @@ export const i18nData = {
"Ocean Breeze": "海蓝", "Ocean Breeze": "海蓝",
"Mint Leaf": "薄荷", "Mint Leaf": "薄荷",
"Golden Glow": "微光", "Golden Glow": "微光",
"Random": "随机",
}, },
}, },
"zh-TW": { "zh-TW": {
@@ -617,6 +620,8 @@ export const i18nData = {
"Ocean Breeze": "海藍", "Ocean Breeze": "海藍",
"Mint Leaf": "薄荷", "Mint Leaf": "薄荷",
"Golden Glow": "微光", "Golden Glow": "微光",
"Random": "隨機",
}, },
}, },
}; };

View File

@@ -613,6 +613,19 @@ class Network {
} }
} }
async getRandomResource(): Promise<Response<Resource>> {
try {
const response = await axios.get(`${this.apiBaseUrl}/resource/random`);
return response.data;
} catch (e: any) {
console.error(e);
return {
success: false,
message: e.toString(),
};
}
}
async deleteResource(id: number): Promise<Response<void>> { async deleteResource(id: number): Promise<Response<void>> {
try { try {
const response = await axios.delete(`${this.apiBaseUrl}/resource/${id}`); const response = await axios.delete(`${this.apiBaseUrl}/resource/${id}`);

View File

@@ -0,0 +1,28 @@
import Loading from "../components/loading.tsx";
import { useNavigate } from "react-router";
import { useEffect } from "react";
import { network } from "../network/network.ts";
import showToast from "../components/toast.ts";
export default function RandomPage() {
const navigate = useNavigate();
useEffect(() => {
network.getRandomResource().then((res) => {
if (res.success) {
navigate(`/resources/${res.data!.id}`, {
state: {
resource: res.data,
},
});
} else {
showToast({
type: "error",
message: res.message || "Failed to fetch random resource",
});
}
});
}, [navigate]);
return <Loading />;
}

View File

@@ -1,4 +1,4 @@
import { useNavigate, useParams } from "react-router"; import { useLocation, useNavigate, useParams } from "react-router";
import { import {
createContext, createContext,
createRef, createRef,
@@ -57,6 +57,8 @@ export default function ResourcePage() {
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const location = useLocation();
const reload = useCallback(async () => { const reload = useCallback(async () => {
if (!isNaN(id)) { if (!isNaN(id)) {
setResource(null); setResource(null);
@@ -76,6 +78,9 @@ export default function ResourcePage() {
useEffect(() => { useEffect(() => {
setResource(null); setResource(null);
if (!isNaN(id)) { if (!isNaN(id)) {
if (location.state) {
setResource(location.state.resource);
} else {
network.getResourceDetails(id).then((res) => { network.getResourceDetails(id).then((res) => {
if (res.success) { if (res.success) {
setResource(res.data!); setResource(res.data!);
@@ -85,7 +90,8 @@ export default function ResourcePage() {
} }
}); });
} }
}, [id]); }
}, [id, location.state]);
const navigate = useNavigate(); const navigate = useNavigate();

View File

@@ -251,12 +251,28 @@ func handleUpdateResource(c fiber.Ctx) error {
}) })
} }
func handleGetRandomResource(c fiber.Ctx) error {
resource, err := service.RandomResource()
if err != nil {
return err
}
if resource == nil {
return model.NewNotFoundError("No resources found")
}
return c.Status(fiber.StatusOK).JSON(model.Response[model.ResourceDetailView]{
Success: true,
Data: *resource,
Message: "Random resource retrieved successfully",
})
}
func AddResourceRoutes(api fiber.Router) { func AddResourceRoutes(api fiber.Router) {
resource := api.Group("/resource") resource := api.Group("/resource")
{ {
resource.Post("/", handleCreateResource) resource.Post("/", handleCreateResource)
resource.Get("/search", handleSearchResources) resource.Get("/search", handleSearchResources)
resource.Get("/", handleListResources) resource.Get("/", handleListResources)
resource.Get("/random", handleGetRandomResource)
resource.Get("/:id", handleGetResource) resource.Get("/:id", handleGetResource)
resource.Delete("/:id", handleDeleteResource) resource.Delete("/:id", handleDeleteResource)
resource.Get("/tag/:tag", handleListResourcesWithTag) resource.Get("/tag/:tag", handleListResourcesWithTag)

View File

@@ -3,6 +3,7 @@ package dao
import ( import (
"errors" "errors"
"github.com/gofiber/fiber/v3/log" "github.com/gofiber/fiber/v3/log"
"math/rand"
"nysoure/server/model" "nysoure/server/model"
"strings" "strings"
"sync" "sync"
@@ -510,3 +511,27 @@ func AddResourceDownloadCount(id uint) error {
stats.downloads.Add(1) stats.downloads.Add(1)
return nil return nil
} }
func RandomResource() (model.Resource, error) {
var maxID int64
if err := db.Model(&model.Resource{}).Select("MAX(id)").Scan(&maxID).Error; err != nil {
return model.Resource{}, err
}
for {
randomID := uint(1 + rand.Int63n(maxID-1))
var resource model.Resource
if err := db.
Preload("User").
Preload("Images").
Preload("Tags").
Preload("Files").
Where("id = ?", randomID).
First(&resource).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
continue // Try again if the resource does not exist
}
return model.Resource{}, err // Return error if any other issue occurs
}
return resource, nil // Return the found resource
}
}

View File

@@ -255,3 +255,12 @@ func EditResource(uid, rid uint, params *ResourceCreateParams) error {
} }
return nil return nil
} }
func RandomResource() (*model.ResourceDetailView, error) {
r, err := dao.RandomResource()
if err != nil {
return nil, err
}
v := r.ToDetailView()
return &v, nil
}