mirror of
https://github.com/usememos/memos.git
synced 2024-11-11 18:14:01 +03:00
feat: update memo visibility in frontend
This commit is contained in:
parent
697d01e306
commit
1afc183458
@ -76,6 +76,13 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
|
||||
memoFind.CreatorID = &userID
|
||||
}
|
||||
|
||||
// Only can get PUBLIC memos in visitor mode
|
||||
_, ok := c.Get(getUserIDContextKey()).(int)
|
||||
if !ok {
|
||||
publicVisibility := api.Public
|
||||
memoFind.Visibility = &publicVisibility
|
||||
}
|
||||
|
||||
rowStatus := api.RowStatus(c.QueryParam("rowStatus"))
|
||||
if rowStatus != "" {
|
||||
memoFind.RowStatus = &rowStatus
|
||||
|
@ -41,7 +41,7 @@ func NewServer(profile *profile.Profile) *Server {
|
||||
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||
Skipper: middleware.DefaultSkipper,
|
||||
Root: "web/dist",
|
||||
Browse: false,
|
||||
Browse: true,
|
||||
HTML5: true,
|
||||
}))
|
||||
|
||||
|
@ -63,12 +63,14 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch user").SetInternal(err)
|
||||
}
|
||||
if user == nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "User not found")
|
||||
|
||||
username := ""
|
||||
if user != nil {
|
||||
username = user.Name
|
||||
}
|
||||
|
||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
|
||||
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(user.Name)); err != nil {
|
||||
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(username)); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user response").SetInternal(err)
|
||||
}
|
||||
return nil
|
||||
|
@ -183,9 +183,7 @@ func patchMemoRaw(db *sql.DB, patch *api.MemoPatch) (*memoRaw, error) {
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
if !row.Next() {
|
||||
return nil, &common.Error{Code: common.NotFound, Err: fmt.Errorf("not found")}
|
||||
}
|
||||
row.Next()
|
||||
|
||||
var memoRaw memoRaw
|
||||
if err := row.Scan(
|
||||
|
1
web/public/icons/visibility.svg
Normal file
1
web/public/icons/visibility.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M24 31.5q3.55 0 6.025-2.475Q32.5 26.55 32.5 23q0-3.55-2.475-6.025Q27.55 14.5 24 14.5q-3.55 0-6.025 2.475Q15.5 19.45 15.5 23q0 3.55 2.475 6.025Q20.45 31.5 24 31.5Zm0-2.9q-2.35 0-3.975-1.625T18.4 23q0-2.35 1.625-3.975T24 17.4q2.35 0 3.975 1.625T29.6 23q0 2.35-1.625 3.975T24 28.6Zm0 9.4q-7.3 0-13.2-4.15Q4.9 29.7 2 23q2.9-6.7 8.8-10.85Q16.7 8 24 8q7.3 0 13.2 4.15Q43.1 16.3 46 23q-2.9 6.7-8.8 10.85Q31.3 38 24 38Zm0-15Zm0 12q6.05 0 11.125-3.275T42.85 23q-2.65-5.45-7.725-8.725Q30.05 11 24 11t-11.125 3.275Q7.8 17.55 5.1 23q2.7 5.45 7.775 8.725Q17.95 35 24 35Z"/></svg>
|
After Width: | Height: | Size: 638 B |
@ -161,6 +161,9 @@ const Memo: React.FC<Props> = (props: Props) => {
|
||||
<Only when={memo.pinned}>
|
||||
<span className="ml-2">PINNED</span>
|
||||
</Only>
|
||||
<Only when={memo.visibility === "PUBLIC"}>
|
||||
<span className="ml-2">PUBLIC</span>
|
||||
</Only>
|
||||
</span>
|
||||
<div className={`btns-container ${userService.isVisitorMode() ? "!hidden" : ""}`}>
|
||||
<span className="btn more-action-btn">
|
||||
|
@ -104,12 +104,27 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
|
||||
editorStateService.setEditMemoWithId(memo.id);
|
||||
}, [memo.id]);
|
||||
|
||||
const handleVisibilityClick = async () => {
|
||||
const visibility = memo.visibility === "PRIVATE" ? "PUBLIC" : "PRIVATE";
|
||||
await memoService.patchMemo({
|
||||
id: memo.id,
|
||||
visibility: visibility,
|
||||
});
|
||||
setMemo({
|
||||
...memo,
|
||||
visibility: visibility,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="memo-card-container">
|
||||
<div className="header-container">
|
||||
<p className="time-text">{utils.getDateTimeString(memo.createdTs)}</p>
|
||||
<div className="btns-container">
|
||||
<button className="btn edit-btn" onClick={handleVisibilityClick}>
|
||||
<img className={`icon-img ${memo.visibility === "PRIVATE" ? "opacity-30" : ""}`} src="/icons/visibility.svg" />
|
||||
</button>
|
||||
<button className="btn edit-btn" onClick={handleEditMemoBtnClick}>
|
||||
<img className="icon-img" src="/icons/edit.svg" />
|
||||
</button>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import * as api from "../helpers/api";
|
||||
import { locationService, userService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
import toastHelper from "./Toast";
|
||||
import showAboutSiteDialog from "./AboutSiteDialog";
|
||||
import "../less/menu-btns-popup.less";
|
||||
@ -12,6 +13,7 @@ interface Props {
|
||||
|
||||
const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
|
||||
const { shownStatus, setShownStatus } = props;
|
||||
const user = useAppSelector((state) => state.user.user);
|
||||
const popupElRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@ -55,10 +57,6 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const handleSignInBtnClick = async () => {
|
||||
locationService.replaceHistory("/signin");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`menu-btns-popup ${shownStatus ? "" : "hidden"}`} ref={popupElRef}>
|
||||
<button className="btn action-btn" onClick={handleAboutBtnClick}>
|
||||
@ -67,9 +65,19 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
|
||||
<button className="btn action-btn" onClick={handlePingBtnClick}>
|
||||
<span className="icon">🎯</span> Ping
|
||||
</button>
|
||||
<button className="btn action-btn" onClick={!userService.isVisitorMode() ? handleSignOutBtnClick : handleSignInBtnClick}>
|
||||
<span className="icon">👋</span> {!userService.isVisitorMode() ? "Sign out" : "Sign in"}
|
||||
</button>
|
||||
{!userService.isVisitorMode() ? (
|
||||
<button className="btn action-btn" onClick={handleSignOutBtnClick}>
|
||||
<span className="icon">👋</span> Sign out
|
||||
</button>
|
||||
) : user ? (
|
||||
<button className="btn action-btn" onClick={() => (window.location.href = "/")}>
|
||||
<span className="icon">🏠</span> Go Back
|
||||
</button>
|
||||
) : (
|
||||
<button className="btn action-btn" onClick={() => (window.location.href = "/signin")}>
|
||||
<span className="icon">👉</span> Sign in
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -22,7 +22,9 @@ const UserBanner: React.FC<Props> = () => {
|
||||
.getUserNameById(currentUserId)
|
||||
.then(({ data }) => {
|
||||
const { data: username } = data;
|
||||
setUsername(username);
|
||||
if (username) {
|
||||
setUsername(username);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
toastHelper.error("User not found");
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { locationService, userService } from "../services";
|
||||
import * as api from "../helpers/api";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import Only from "../components/common/OnlyWhen";
|
||||
import Sidebar from "../components/Sidebar";
|
||||
@ -7,6 +8,7 @@ import MemosHeader from "../components/MemosHeader";
|
||||
import MemoEditor from "../components/MemoEditor";
|
||||
import MemoFilter from "../components/MemoFilter";
|
||||
import MemoList from "../components/MemoList";
|
||||
import toastHelper from "../components/Toast";
|
||||
import "../less/home.less";
|
||||
|
||||
function Home() {
|
||||
@ -16,10 +18,20 @@ function Home() {
|
||||
userService
|
||||
.doSignIn()
|
||||
.catch()
|
||||
.finally(() => {
|
||||
if (!userService.isVisitorMode() && !userService.getState().user) {
|
||||
locationService.replaceHistory("/signin");
|
||||
return;
|
||||
.finally(async () => {
|
||||
if (!userService.getState().user) {
|
||||
if (userService.isVisitorMode()) {
|
||||
const currentUserId = userService.getUserIdFromPath() as number;
|
||||
const {
|
||||
data: { data: username },
|
||||
} = await api.getUserNameById(currentUserId);
|
||||
if (!username) {
|
||||
toastHelper.error("User not found");
|
||||
}
|
||||
} else {
|
||||
locationService.replaceHistory("/signin");
|
||||
return;
|
||||
}
|
||||
}
|
||||
loadingState.setFinish();
|
||||
});
|
||||
|
5
web/src/types/modules/memo.d.ts
vendored
5
web/src/types/modules/memo.d.ts
vendored
@ -1,5 +1,7 @@
|
||||
type MemoId = number;
|
||||
|
||||
type Visibility = "PUBLIC" | "PRIVATE";
|
||||
|
||||
interface Memo {
|
||||
id: MemoId;
|
||||
|
||||
@ -9,6 +11,7 @@ interface Memo {
|
||||
rowStatus: RowStatus;
|
||||
|
||||
content: string;
|
||||
visibility: Visibility;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
@ -21,9 +24,11 @@ interface MemoPatch {
|
||||
id: MemoId;
|
||||
content?: string;
|
||||
rowStatus?: RowStatus;
|
||||
visibility?: Visibility;
|
||||
}
|
||||
|
||||
interface MemoFind {
|
||||
creatorId?: UserId;
|
||||
rowStatus?: RowStatus;
|
||||
visibility?: Visibility;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user