feat: complete im p2p relationship onboarding
This commit is contained in:
+143
-2
@@ -1014,10 +1014,39 @@ func serveIMConversationContext(wrt http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
func serveIMFriendRequests(wrt http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
switch req.Method {
|
||||
case http.MethodGet:
|
||||
serveIMFriendRequestList(wrt, req)
|
||||
case http.MethodPost:
|
||||
serveIMFriendRequestCreate(wrt, req)
|
||||
default:
|
||||
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
|
||||
}
|
||||
}
|
||||
|
||||
func serveIMFriendRequestList(wrt http.ResponseWriter, req *http.Request) {
|
||||
principal, ok := authenticateIMHTTPBearer(wrt, req)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
limit := parseIMLimit(req.URL.Query().Get("limit"), 20, 100)
|
||||
items, nextCursor, err := store.Relations.ListFriendRequests(principal.TenantID, principal.Uid,
|
||||
strings.TrimSpace(req.URL.Query().Get("direction")),
|
||||
strings.TrimSpace(req.URL.Query().Get("status")),
|
||||
strings.TrimSpace(req.URL.Query().Get("cursor")),
|
||||
limit)
|
||||
if err != nil {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
}
|
||||
payload := make([]map[string]any, 0, len(items))
|
||||
for _, item := range items {
|
||||
payload = append(payload, friendRequestListItemPayload(item))
|
||||
}
|
||||
writeIMSuccess(wrt, req, map[string]any{"items": payload, "next_cursor": nextCursor})
|
||||
}
|
||||
|
||||
func serveIMFriendRequestCreate(wrt http.ResponseWriter, req *http.Request) {
|
||||
principal, ok := authenticateIMHTTPBearer(wrt, req)
|
||||
if !ok {
|
||||
return
|
||||
@@ -1060,6 +1089,32 @@ func serveIMFriendRequests(wrt http.ResponseWriter, req *http.Request) {
|
||||
writeIMSuccess(wrt, req, friendRequestPayload(created))
|
||||
}
|
||||
|
||||
func serveIMFriends(wrt http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
|
||||
return
|
||||
}
|
||||
principal, ok := authenticateIMHTTPBearer(wrt, req)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
limit := parseIMLimit(req.URL.Query().Get("limit"), 50, 100)
|
||||
items, nextCursor, err := store.Relations.ListFriends(principal.TenantID, principal.Uid,
|
||||
strings.TrimSpace(req.URL.Query().Get("query")),
|
||||
strings.TrimSpace(req.URL.Query().Get("initial")),
|
||||
strings.TrimSpace(req.URL.Query().Get("cursor")),
|
||||
limit)
|
||||
if err != nil {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
}
|
||||
payload := make([]map[string]any, 0, len(items))
|
||||
for _, item := range items {
|
||||
payload = append(payload, friendListItemPayload(item))
|
||||
}
|
||||
writeIMSuccess(wrt, req, map[string]any{"items": payload, "next_cursor": nextCursor})
|
||||
}
|
||||
|
||||
func serveIMFriendRequestAction(wrt http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodPost {
|
||||
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
|
||||
@@ -1615,7 +1670,9 @@ func friendRequestPayload(req *types.FriendRequest) map[string]any {
|
||||
"status": req.Status,
|
||||
"created_at": req.CreatedAt.UTC().Format(time.RFC3339Nano),
|
||||
"expires_at": req.ExpiresAt.UTC().Format(time.RFC3339Nano),
|
||||
"topic": req.ApplicantID.P2PName(req.TargetID),
|
||||
}
|
||||
if topic := req.ApplicantID.P2PName(req.TargetID); topic != "" {
|
||||
data["topic"] = topic
|
||||
}
|
||||
if req.SourceTopic != "" {
|
||||
data["source_topic"] = req.SourceTopic
|
||||
@@ -1626,6 +1683,90 @@ func friendRequestPayload(req *types.FriendRequest) map[string]any {
|
||||
return data
|
||||
}
|
||||
|
||||
func friendRequestListItemPayload(item types.FriendRequestListItem) map[string]any {
|
||||
data := friendRequestPayload(&item.Request)
|
||||
data["direction"] = item.Direction
|
||||
if item.Peer != nil {
|
||||
data["peer"] = userSummaryPayload(item.Peer, "")
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func friendListItemPayload(item types.FriendListItem) map[string]any {
|
||||
data := map[string]any{
|
||||
"user_id": item.Friendship.PeerID.UserId(),
|
||||
"relationship": item.Friendship.Status,
|
||||
"remark": item.Friendship.Remark,
|
||||
"topic": item.Friendship.Topic,
|
||||
"online_visibility": "unknown",
|
||||
"created_at": item.Friendship.CreatedAt.UTC().Format(time.RFC3339Nano),
|
||||
"updated_at": item.Friendship.UpdatedAt.UTC().Format(time.RFC3339Nano),
|
||||
"capabilities": map[string]any{"can_send": item.Friendship.Status == types.RelationshipFriend, "can_call": item.Friendship.Status == types.RelationshipFriend},
|
||||
"display_name_source": "profile",
|
||||
}
|
||||
if item.Friendship.Remark != "" {
|
||||
data["display_name"] = item.Friendship.Remark
|
||||
data["display_name_source"] = "remark"
|
||||
}
|
||||
if item.Peer != nil {
|
||||
for key, value := range userSummaryPayload(item.Peer, item.Friendship.Remark) {
|
||||
_, exists := data[key]
|
||||
if !exists || key == "avatar_file_id" || (key == "display_name" && item.Friendship.Remark == "") {
|
||||
data[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
data["initial"] = displayInitial(data["display_name"])
|
||||
return data
|
||||
}
|
||||
|
||||
func userSummaryPayload(user *types.User, remark string) map[string]any {
|
||||
uid := types.ParseUid(user.Id)
|
||||
userID := uid.UserId()
|
||||
if uid.IsZero() {
|
||||
userID = user.Id
|
||||
}
|
||||
data := map[string]any{"user_id": userID}
|
||||
if remark != "" {
|
||||
data["display_name"] = remark
|
||||
}
|
||||
if public, ok := user.Public.(map[string]any); ok {
|
||||
copyUserPublicSummary(data, public)
|
||||
} else if public, ok := user.Public.(types.KVMap); ok {
|
||||
copyUserPublicSummary(data, map[string]any(public))
|
||||
}
|
||||
if _, ok := data["display_name"]; !ok {
|
||||
data["display_name"] = userID
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func copyUserPublicSummary(data map[string]any, public map[string]any) {
|
||||
for _, key := range []string{"display_name", "fn", "name", "nickname"} {
|
||||
if value, ok := public[key]; ok && strings.TrimSpace(fmt.Sprint(value)) != "" {
|
||||
data["display_name"] = value
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"avatar_file_id", "photo", "avatar"} {
|
||||
if value, ok := public[key]; ok && strings.TrimSpace(fmt.Sprint(value)) != "" {
|
||||
data["avatar_file_id"] = value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func displayInitial(value any) string {
|
||||
name := strings.TrimSpace(fmt.Sprint(value))
|
||||
if name == "" {
|
||||
return "#"
|
||||
}
|
||||
for _, r := range name {
|
||||
return strings.ToUpper(string(r))
|
||||
}
|
||||
return "#"
|
||||
}
|
||||
|
||||
func writeIMRelationError(wrt http.ResponseWriter, req *http.Request, err error) {
|
||||
switch {
|
||||
case errors.Is(err, types.ErrNotFound):
|
||||
|
||||
Reference in New Issue
Block a user