Commit 3ad08325 authored by Steven's avatar Steven

chore: use user v2 api in frontend

parent 93f062d0
......@@ -66,7 +66,7 @@ func (s *UserService) UpdateUser(ctx context.Context, request *apiv2pb.UpdateUse
if currentUser == nil || (currentUser.ID != userID && currentUser.Role != store.RoleAdmin) {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
if request.UpdateMask == nil || len(request.UpdateMask) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "update mask is empty")
}
......@@ -75,7 +75,7 @@ func (s *UserService) UpdateUser(ctx context.Context, request *apiv2pb.UpdateUse
ID: userID,
UpdatedTs: &currentTs,
}
for _, path := range request.UpdateMask.Paths {
for _, path := range request.UpdateMask {
if path == "username" {
update.Username = &request.User.Username
} else if path == "nickname" {
......
......@@ -7,7 +7,6 @@ import "api/v2/memo_service.proto";
import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";
option go_package = "gen/api/v2";
......@@ -18,7 +17,10 @@ service UserService {
option (google.api.method_signature) = "username";
}
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse) {
option (google.api.http) = {patch: "/api/v2/users/{username}"};
option (google.api.http) = {
post: "/api/v2/users/{username}"
body: "*"
};
option (google.api.method_signature) = "username";
}
}
......@@ -68,7 +70,7 @@ message UpdateUserRequest {
User user = 2;
// The update mask applies to the user resource.
google.protobuf.FieldMask update_mask = 3;
repeated string update_mask = 3;
}
message UpdateUserResponse {
......
......@@ -429,7 +429,7 @@
| ----- | ---- | ----- | ----------- |
| username | [string](#string) | | |
| user | [User](#memos-api-v2-User) | | |
| update_mask | [google.protobuf.FieldMask](#google-protobuf-FieldMask) | | The update mask applies to the user resource. |
| update_mask | [string](#string) | repeated | The update mask applies to the user resource. |
......
This diff is collapsed.
......@@ -83,14 +83,18 @@ func local_request_UserService_GetUser_0(ctx context.Context, marshaler runtime.
}
var (
filter_UserService_UpdateUser_0 = &utilities.DoubleArray{Encoding: map[string]int{"username": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}}
)
func request_UserService_UpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateUserRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
......@@ -108,13 +112,6 @@ func request_UserService_UpdateUser_0(ctx context.Context, marshaler runtime.Mar
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUser_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdateUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
......@@ -124,6 +121,14 @@ func local_request_UserService_UpdateUser_0(ctx context.Context, marshaler runti
var protoReq UpdateUserRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
......@@ -141,13 +146,6 @@ func local_request_UserService_UpdateUser_0(ctx context.Context, marshaler runti
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUser_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UpdateUser(ctx, &protoReq)
return msg, metadata, err
......@@ -184,7 +182,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
})
mux.Handle("PATCH", pattern_UserService_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
mux.Handle("POST", pattern_UserService_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
......@@ -272,7 +270,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
})
mux.Handle("PATCH", pattern_UserService_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
mux.Handle("POST", pattern_UserService_UpdateUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
......
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { useGlobalStore, useUserStore } from "@/store/module";
import { useUserV1Store } from "@/store/v1";
import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog";
import Icon from "./Icon";
......@@ -10,6 +11,7 @@ type Props = DialogProps;
const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
const t = useTranslate();
const userStore = useUserStore();
const userV1Store = useUserV1Store();
const globalStore = useGlobalStore();
const profile = globalStore.state.systemStatus.profile;
const [newPassword, setNewPassword] = useState("");
......@@ -50,10 +52,13 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
try {
const user = userStore.getState().user as User;
await userStore.patchUser({
id: user.id,
password: newPassword,
});
await userV1Store.updateUser(
{
username: user.username,
password: newPassword,
},
["password"]
);
toast.success(t("message.password-changed"));
handleCloseBtnClick();
} catch (error: any) {
......
import { Button, Input, Textarea } from "@mui/joy";
import { useUserStore } from "@/store/module";
import useCurrentUser from "@/hooks/useCurrentUser";
import { useUserV1Store } from "@/store/v1";
import { useTranslate } from "@/utils/i18n";
import showChangePasswordDialog from "../ChangePasswordDialog";
import { showCommonDialog } from "../Dialog/CommonDialog";
......@@ -9,8 +10,8 @@ import UserAvatar from "../UserAvatar";
const MyAccountSection = () => {
const t = useTranslate();
const userStore = useUserStore();
const user = userStore.state.user as User;
const userV1Store = useUserV1Store();
const user = useCurrentUser();
const openAPIRoute = `${window.location.origin}/api/v1/memo?openId=${user.openId}`;
const handleResetOpenIdBtnClick = async () => {
......@@ -20,10 +21,12 @@ const MyAccountSection = () => {
style: "warning",
dialogName: "reset-openid-dialog",
onConfirm: async () => {
await userStore.patchUser({
id: user.id,
resetOpenId: true,
});
await userV1Store.updateUser(
{
username: user.username,
},
["reset_open_id"]
);
},
});
};
......
import { useEffect } from "react";
import { useUserStore } from "@/store/module";
import { useUserV1Store } from "@/store/v1";
const useCurrentUser = () => {
const userStore = useUserStore();
const userV1Store = useUserV1Store();
const currentUsername = userStore.getCurrentUsername();
useEffect(() => {
if (currentUsername) {
userV1Store.getOrFetchUserByUsername(currentUsername);
}
}, [currentUsername]);
return userV1Store.getUserByUsername(currentUsername);
};
export default useCurrentUser;
import axios from "axios";
import { create } from "zustand";
import * as api from "@/helpers/api";
import { User } from "@/types/proto/api/v2/user_service_pb";
import { UpdateUserResponse, User } from "@/types/proto/api/v2/user_service_pb";
interface UserV1Store {
userMapByUsername: Record<string, User>;
getOrFetchUserByUsername: (username: string) => Promise<User>;
getUserByUsername: (username: string) => User;
updateUser: (user: Partial<User>, updateMask: string[]) => Promise<User>;
}
// Request cache is used to prevent multiple requests.
......@@ -39,6 +41,21 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
const userMap = get().userMapByUsername;
return userMap[username] as User;
},
updateUser: async (user: Partial<User>, updateMask: string[]) => {
const {
data: { user: updatedUser },
} = await axios.post<UpdateUserResponse>(`/api/v2/users/${user.username}`, {
user,
updateMask,
});
if (!updatedUser) {
throw new Error("User not found");
}
const userMap = get().userMapByUsername;
userMap[updatedUser.username] = updatedUser;
set(userMap);
return updatedUser;
},
}));
export default useUserV1Store;
......@@ -3,7 +3,7 @@
/* eslint-disable */
// @ts-nocheck
import type { BinaryReadOptions, FieldList, FieldMask, JsonReadOptions, JsonValue, PartialMessage, PlainMessage, Timestamp } from "@bufbuild/protobuf";
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage, Timestamp } from "@bufbuild/protobuf";
import { Message, proto3 } from "@bufbuild/protobuf";
import type { RowStatus } from "./common_pb.js";
import type { Visibility } from "./memo_service_pb.js";
......@@ -172,9 +172,9 @@ export declare class UpdateUserRequest extends Message<UpdateUserRequest> {
/**
* The update mask applies to the user resource.
*
* @generated from field: google.protobuf.FieldMask update_mask = 3;
* @generated from field: repeated string update_mask = 3;
*/
updateMask?: FieldMask;
updateMask: string[];
constructor(data?: PartialMessage<UpdateUserRequest>);
......
......@@ -3,7 +3,7 @@
/* eslint-disable */
// @ts-nocheck
import { FieldMask, proto3, Timestamp } from "@bufbuild/protobuf";
import { proto3, Timestamp } from "@bufbuild/protobuf";
import { RowStatus } from "./common_pb.js";
import { Visibility } from "./memo_service_pb.js";
......@@ -68,7 +68,7 @@ export const UpdateUserRequest = proto3.makeMessageType(
() => [
{ no: 1, name: "username", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "user", kind: "message", T: User },
{ no: 3, name: "update_mask", kind: "message", T: FieldMask },
{ no: 3, name: "update_mask", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true },
],
);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment