mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 12:17:24 +00:00
Add Random resource retrieval and navigation.
This commit is contained in:
@@ -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>
|
||||||
|
@@ -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");
|
||||||
|
@@ -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": "隨機",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -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}`);
|
||||||
|
28
frontend/src/pages/random_page.tsx
Normal file
28
frontend/src/pages/random_page.tsx
Normal 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 />;
|
||||||
|
}
|
@@ -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,16 +78,20 @@ export default function ResourcePage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setResource(null);
|
setResource(null);
|
||||||
if (!isNaN(id)) {
|
if (!isNaN(id)) {
|
||||||
network.getResourceDetails(id).then((res) => {
|
if (location.state) {
|
||||||
if (res.success) {
|
setResource(location.state.resource);
|
||||||
setResource(res.data!);
|
} else {
|
||||||
document.title = res.data!.title;
|
network.getResourceDetails(id).then((res) => {
|
||||||
} else {
|
if (res.success) {
|
||||||
showToast({ message: res.message, type: "error" });
|
setResource(res.data!);
|
||||||
}
|
document.title = res.data!.title;
|
||||||
});
|
} else {
|
||||||
|
showToast({ message: res.message, type: "error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [id]);
|
}, [id, location.state]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user