feat: update memo visibility in frontend

This commit is contained in:
boojack 2022-07-08 23:38:24 +08:00
parent 697d01e306
commit 1afc183458
11 changed files with 72 additions and 19 deletions

View File

@ -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

View File

@ -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,
}))

View File

@ -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

View File

@ -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(

View 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

View File

@ -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">

View File

@ -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>

View File

@ -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>
);
};

View File

@ -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");

View File

@ -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();
});

View File

@ -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;
}