mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 04:17:23 +00:00
Add new file activity.
This commit is contained in:
@@ -234,6 +234,7 @@ export const i18nData = {
|
|||||||
"Comment Details": "Comment Details",
|
"Comment Details": "Comment Details",
|
||||||
"Posted a comment": "Posted a comment",
|
"Posted a comment": "Posted a comment",
|
||||||
"Resources": "Resources",
|
"Resources": "Resources",
|
||||||
|
"Added a new file": "Added a new file",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-CN": {
|
"zh-CN": {
|
||||||
@@ -461,6 +462,7 @@ export const i18nData = {
|
|||||||
"Posted a comment": "发布了一个评论",
|
"Posted a comment": "发布了一个评论",
|
||||||
|
|
||||||
"Resources": "资源",
|
"Resources": "资源",
|
||||||
|
"Added a new file": "添加了新文件",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-TW": {
|
"zh-TW": {
|
||||||
@@ -688,6 +690,7 @@ export const i18nData = {
|
|||||||
"Posted a comment": "發布了評論",
|
"Posted a comment": "發布了評論",
|
||||||
|
|
||||||
"Resources": "資源",
|
"Resources": "資源",
|
||||||
|
"Added a new file": "添加了新檔案",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -177,6 +177,7 @@ export enum ActivityType {
|
|||||||
ResourcePublished = 1,
|
ResourcePublished = 1,
|
||||||
ResourceUpdated = 2,
|
ResourceUpdated = 2,
|
||||||
NewComment = 3,
|
NewComment = 3,
|
||||||
|
NewFile = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Activity {
|
export interface Activity {
|
||||||
@@ -187,4 +188,5 @@ export interface Activity {
|
|||||||
resource?: Resource;
|
resource?: Resource;
|
||||||
user?: User;
|
user?: User;
|
||||||
comment?: Comment;
|
comment?: Comment;
|
||||||
|
file?: RFile;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import Loading from "../components/loading.tsx";
|
import Loading from "../components/loading.tsx";
|
||||||
import { CommentContent } from "../components/comment_tile.tsx";
|
import { CommentContent } from "../components/comment_tile.tsx";
|
||||||
|
import {MdOutlineArchive, MdOutlinePhotoAlbum} from "react-icons/md";
|
||||||
|
import Badge from "../components/badge.tsx";
|
||||||
|
|
||||||
export default function ActivitiesPage() {
|
export default function ActivitiesPage() {
|
||||||
const [activities, setActivities] = useState<Activity[]>([]);
|
const [activities, setActivities] = useState<Activity[]>([]);
|
||||||
@@ -59,6 +61,18 @@ export default function ActivitiesPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fileSizeToString(size: number) {
|
||||||
|
if (size < 1024) {
|
||||||
|
return size + "B";
|
||||||
|
} else if (size < 1024 * 1024) {
|
||||||
|
return (size / 1024).toFixed(2) + "KB";
|
||||||
|
} else if (size < 1024 * 1024 * 1024) {
|
||||||
|
return (size / 1024 / 1024).toFixed(2) + "MB";
|
||||||
|
} else {
|
||||||
|
return (size / 1024 / 1024 / 1024).toFixed(2) + "GB";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ActivityCard({ activity }: { activity: Activity }) {
|
function ActivityCard({ activity }: { activity: Activity }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -67,6 +81,7 @@ function ActivityCard({ activity }: { activity: Activity }) {
|
|||||||
t("Published a resource"),
|
t("Published a resource"),
|
||||||
t("Updated a resource"),
|
t("Updated a resource"),
|
||||||
t("Posted a comment"),
|
t("Posted a comment"),
|
||||||
|
t("Added a new file"),
|
||||||
];
|
];
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -97,6 +112,33 @@ function ActivityCard({ activity }: { activity: Activity }) {
|
|||||||
<CommentContent content={activity.comment!.content} />
|
<CommentContent content={activity.comment!.content} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (activity.type === ActivityType.NewFile) {
|
||||||
|
content = (
|
||||||
|
<div>
|
||||||
|
<h4 className={"font-bold py-2"}>{activity.file!.filename}</h4>
|
||||||
|
<p className={"text-sm whitespace-pre-wrap"}>
|
||||||
|
{activity.file!.description}
|
||||||
|
</p>
|
||||||
|
<p className={"pt-1"}>
|
||||||
|
<Badge className={"badge-soft badge-secondary text-xs mr-2"}>
|
||||||
|
<MdOutlineArchive size={16} className={"inline-block"} />
|
||||||
|
{activity.file!.is_redirect
|
||||||
|
? t("Redirect")
|
||||||
|
: fileSizeToString(activity.file!.size)}
|
||||||
|
</Badge>
|
||||||
|
<Badge className={"badge-soft badge-accent text-xs mr-2"}>
|
||||||
|
<MdOutlinePhotoAlbum size={16} className={"inline-block"} />
|
||||||
|
{(() => {
|
||||||
|
let title = activity.resource!.title;
|
||||||
|
if (title.length > 20) {
|
||||||
|
title = title.slice(0, 20) + "...";
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
})()}
|
||||||
|
</Badge>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -112,6 +154,8 @@ function ActivityCard({ activity }: { activity: Activity }) {
|
|||||||
navigate(`/resources/${activity.resource?.id}`);
|
navigate(`/resources/${activity.resource?.id}`);
|
||||||
} else if (activity.type === ActivityType.NewComment) {
|
} else if (activity.type === ActivityType.NewComment) {
|
||||||
navigate(`/comments/${activity.comment?.id}`);
|
navigate(`/comments/${activity.comment?.id}`);
|
||||||
|
} else if (activity.type === ActivityType.NewFile) {
|
||||||
|
navigate(`/resources/${activity.resource?.id}#files`);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@@ -171,7 +171,10 @@ export default function ManageServerConfigPage() {
|
|||||||
checked={config.allow_normal_user_upload}
|
checked={config.allow_normal_user_upload}
|
||||||
className="toggle-primary toggle"
|
className="toggle-primary toggle"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setConfig({ ...config, allow_normal_user_upload: e.target.checked });
|
setConfig({
|
||||||
|
...config,
|
||||||
|
allow_normal_user_upload: e.target.checked,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@@ -145,7 +145,9 @@ export default function StorageView() {
|
|||||||
<tr key={s.id} className={"hover"}>
|
<tr key={s.id} className={"hover"}>
|
||||||
<td>
|
<td>
|
||||||
{s.name}
|
{s.name}
|
||||||
{s.isDefault && <Badge className={"ml-1"}>{t("Default")}</Badge>}
|
{s.isDefault && (
|
||||||
|
<Badge className={"ml-1"}>{t("Default")}</Badge>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>{new Date(s.createdAt).toLocaleString()}</td>
|
<td>{new Date(s.createdAt).toLocaleString()}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -173,15 +175,15 @@ export default function StorageView() {
|
|||||||
>
|
>
|
||||||
<a>{t("Delete")}</a>
|
<a>{t("Delete")}</a>
|
||||||
</PopupMenuItem>
|
</PopupMenuItem>
|
||||||
{!s.isDefault && <PopupMenuItem
|
{!s.isDefault && (
|
||||||
onClick={() => {
|
<PopupMenuItem
|
||||||
handleSetDefault(s.id);
|
onClick={() => {
|
||||||
}}
|
handleSetDefault(s.id);
|
||||||
>
|
}}
|
||||||
<a>
|
>
|
||||||
t("Set as Default")
|
<a>t("Set as Default")</a>
|
||||||
</a>
|
</PopupMenuItem>
|
||||||
</PopupMenuItem>}
|
)}
|
||||||
</ul>,
|
</ul>,
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
`set_default_button_${s.id}`,
|
`set_default_button_${s.id}`,
|
||||||
|
@@ -395,7 +395,7 @@ function DeleteResourceDialog({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = createContext<() => void>(() => { });
|
const context = createContext<() => void>(() => {});
|
||||||
|
|
||||||
function Article({ resource }: { resource: ResourceDetails }) {
|
function Article({ resource }: { resource: ResourceDetails }) {
|
||||||
return (
|
return (
|
||||||
@@ -503,10 +503,7 @@ function Article({ resource }: { resource: ResourceDetails }) {
|
|||||||
const href = props.href as string;
|
const href = props.href as string;
|
||||||
const origin = window.location.origin;
|
const origin = window.location.origin;
|
||||||
|
|
||||||
if (
|
if (href.startsWith(origin) || href.startsWith("/")) {
|
||||||
href.startsWith(origin) ||
|
|
||||||
href.startsWith("/")
|
|
||||||
) {
|
|
||||||
let path = href;
|
let path = href;
|
||||||
if (path.startsWith(origin)) {
|
if (path.startsWith(origin)) {
|
||||||
path = path.substring(origin.length);
|
path = path.substring(origin.length);
|
||||||
@@ -532,7 +529,13 @@ function Article({ resource }: { resource: ResourceDetails }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RelatedResourceCard({ r, content }: { r: Resource, content?: string }) {
|
function RelatedResourceCard({
|
||||||
|
r,
|
||||||
|
content,
|
||||||
|
}: {
|
||||||
|
r: Resource;
|
||||||
|
content?: string;
|
||||||
|
}) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [articleWidth, setArticleWidth] = useState<number | null>(null);
|
const [articleWidth, setArticleWidth] = useState<number | null>(null);
|
||||||
@@ -549,10 +552,9 @@ function RelatedResourceCard({ r, content }: { r: Resource, content?: string })
|
|||||||
if (articleElement) {
|
if (articleElement) {
|
||||||
observer.observe(articleElement);
|
observer.observe(articleElement);
|
||||||
}
|
}
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const imgHeight =
|
const imgHeight = r.image && r.image.width > r.image.height ? 320 : 420;
|
||||||
r.image && r.image.width > r.image.height ? 320 : 420;
|
|
||||||
let imgWidth = r.image
|
let imgWidth = r.image
|
||||||
? (r.image.width / r.image.height) * imgHeight
|
? (r.image.width / r.image.height) * imgHeight
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -561,7 +563,7 @@ function RelatedResourceCard({ r, content }: { r: Resource, content?: string })
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!articleWidth) {
|
if (!articleWidth) {
|
||||||
return <></>
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -29,6 +29,15 @@ func AddNewCommentActivity(userID, commentID uint) error {
|
|||||||
return db.Create(activity).Error
|
return db.Create(activity).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddNewFileActivity(userID, fileID uint) error {
|
||||||
|
activity := &model.Activity{
|
||||||
|
UserID: userID,
|
||||||
|
Type: model.ActivityTypeNewFile,
|
||||||
|
RefID: fileID,
|
||||||
|
}
|
||||||
|
return db.Create(activity).Error
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteResourceActivity(resourceID uint) error {
|
func DeleteResourceActivity(resourceID uint) error {
|
||||||
return db.Where("ref_id = ? AND (type = ? OR type = ?)", resourceID, model.ActivityTypeNewResource, model.ActivityTypeUpdateResource).Delete(&model.Activity{}).Error
|
return db.Where("ref_id = ? AND (type = ? OR type = ?)", resourceID, model.ActivityTypeNewResource, model.ActivityTypeUpdateResource).Delete(&model.Activity{}).Error
|
||||||
}
|
}
|
||||||
|
@@ -106,6 +106,8 @@ func CreateFile(filename string, description string, resourceID uint, storageID
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = AddNewFileActivity(userID, f.ID)
|
||||||
|
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +122,17 @@ func GetFile(id string) (*model.File, error) {
|
|||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFileByID(id uint) (*model.File, error) {
|
||||||
|
f := &model.File{}
|
||||||
|
if err := db.Preload("Storage").Where("id = ?", id).First(f).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, model.NewNotFoundError("file not found")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteFile(id string) error {
|
func DeleteFile(id string) error {
|
||||||
f := &model.File{}
|
f := &model.File{}
|
||||||
if err := db.Where("uuid = ?", id).First(f).Error; err != nil {
|
if err := db.Where("uuid = ?", id).First(f).Error; err != nil {
|
||||||
@@ -130,8 +143,21 @@ func DeleteFile(id string) error {
|
|||||||
if err := tx.Delete(f).Error; err != nil {
|
if err := tx.Delete(f).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return tx.Model(&model.User{}).Where("id = ?", f.UserID).
|
if err := tx.
|
||||||
UpdateColumn("files_count", gorm.Expr("files_count - ?", 1)).Error
|
Model(&model.User{}).
|
||||||
|
Where("id = ?", f.UserID).
|
||||||
|
UpdateColumn("files_count", gorm.Expr("files_count - ?", 1)).
|
||||||
|
Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.
|
||||||
|
Model(&model.Activity{}).
|
||||||
|
Where("type = ? AND ref_id = ?", model.ActivityTypeNewFile, f.ID).
|
||||||
|
Delete(&model.Activity{}).
|
||||||
|
Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -122,6 +122,9 @@ func DeleteResource(id uint) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := tx.Unscoped().Model(&model.File{}).Where("resource_id = ?", id).Delete(&model.File{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := tx.Model(&model.User{}).Where("id = ?", r.UserID).Update("resources_count", gorm.Expr("resources_count - ?", 1)).Error; err != nil {
|
if err := tx.Model(&model.User{}).Where("id = ?", r.UserID).Update("resources_count", gorm.Expr("resources_count - ?", 1)).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -540,7 +543,10 @@ func RandomResource() (model.Resource, error) {
|
|||||||
return model.Resource{}, err
|
return model.Resource{}, err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
randomID := uint(1 + rand.Int63n(maxID-1))
|
randomID := uint(1)
|
||||||
|
if maxID > 1 {
|
||||||
|
randomID = uint(1 + rand.Int63n(maxID-1))
|
||||||
|
}
|
||||||
var resource model.Resource
|
var resource model.Resource
|
||||||
if err := db.
|
if err := db.
|
||||||
Preload("User").
|
Preload("User").
|
||||||
|
@@ -13,6 +13,7 @@ const (
|
|||||||
ActivityTypeNewResource
|
ActivityTypeNewResource
|
||||||
ActivityTypeUpdateResource
|
ActivityTypeUpdateResource
|
||||||
ActivityTypeNewComment
|
ActivityTypeNewComment
|
||||||
|
ActivityTypeNewFile
|
||||||
)
|
)
|
||||||
|
|
||||||
type Activity struct {
|
type Activity struct {
|
||||||
@@ -29,4 +30,5 @@ type ActivityView struct {
|
|||||||
User UserView `json:"user"`
|
User UserView `json:"user"`
|
||||||
Comment *CommentView `json:"comment,omitempty"`
|
Comment *CommentView `json:"comment,omitempty"`
|
||||||
Resource *ResourceView `json:"resource,omitempty"`
|
Resource *ResourceView `json:"resource,omitempty"`
|
||||||
|
File *FileView `json:"file,omitempty"`
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ func GetActivityList(page int) ([]model.ActivityView, int, error) {
|
|||||||
}
|
}
|
||||||
var comment *model.CommentView
|
var comment *model.CommentView
|
||||||
var resource *model.ResourceView
|
var resource *model.ResourceView
|
||||||
|
var file *model.FileView
|
||||||
switch activity.Type {
|
switch activity.Type {
|
||||||
case model.ActivityTypeNewComment:
|
case model.ActivityTypeNewComment:
|
||||||
c, err := dao.GetCommentByID(activity.RefID)
|
c, err := dao.GetCommentByID(activity.RefID)
|
||||||
@@ -37,6 +38,19 @@ func GetActivityList(page int) ([]model.ActivityView, int, error) {
|
|||||||
}
|
}
|
||||||
rv := r.ToView()
|
rv := r.ToView()
|
||||||
resource = &rv
|
resource = &rv
|
||||||
|
case model.ActivityTypeNewFile:
|
||||||
|
f, err := dao.GetFileByID(activity.RefID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
fv := f.ToView()
|
||||||
|
file = fv
|
||||||
|
r, err := dao.GetResourceByID(f.ResourceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
rv := r.ToView()
|
||||||
|
resource = &rv
|
||||||
}
|
}
|
||||||
view := model.ActivityView{
|
view := model.ActivityView{
|
||||||
ID: activity.ID,
|
ID: activity.ID,
|
||||||
@@ -45,6 +59,7 @@ func GetActivityList(page int) ([]model.ActivityView, int, error) {
|
|||||||
Time: activity.CreatedAt,
|
Time: activity.CreatedAt,
|
||||||
Comment: comment,
|
Comment: comment,
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
|
File: file,
|
||||||
}
|
}
|
||||||
views = append(views, view)
|
views = append(views, view)
|
||||||
}
|
}
|
||||||
|
@@ -176,6 +176,13 @@ func DeleteResource(uid, id uint) error {
|
|||||||
return model.NewUnAuthorizedError("You have not permission to delete this resource")
|
return model.NewUnAuthorizedError("You have not permission to delete this resource")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r, err := GetResource(id, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(r.Files) > 0 {
|
||||||
|
return model.NewRequestError("This resource has files, please delete them first")
|
||||||
|
}
|
||||||
if err := dao.DeleteResource(id); err != nil {
|
if err := dao.DeleteResource(id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user