From 6bb93c579e03a6bae8c3febf3fc3a072d211697d Mon Sep 17 00:00:00 2001 From: sunface Date: Thu, 11 Mar 2021 15:03:50 +0800 Subject: [PATCH] add following/follower --- pages/[username]/index.tsx | 104 ++++++++++++------ pages/follow/followers.tsx | 79 ------------- pages/follow/users.tsx | 79 ------------- pages/interaction/followers.tsx | 52 +++++++++ .../following-tags.tsx} | 4 +- pages/interaction/following-users.tsx | 59 ++++++++++ server/internal/api/interaction.go | 26 ++++- server/internal/api/user.go | 13 +++ server/internal/cache/cache.go | 12 +- server/internal/interaction/follow.go | 32 ++++++ server/internal/search/search.go | 5 +- server/internal/server.go | 2 + server/internal/user/users.go | 20 +++- server/pkg/common/reserve_urls.go | 2 +- server/pkg/models/cache.go | 4 + server/pkg/models/user.go | 43 ++++---- src/components/card.tsx | 1 + src/components/user-menu.tsx | 2 +- src/components/users/user-card.tsx | 3 +- src/data/links.tsx | 8 +- src/data/reserve-urls.ts | 2 +- src/types/user.ts | 3 +- 22 files changed, 319 insertions(+), 236 deletions(-) delete mode 100644 pages/follow/followers.tsx delete mode 100644 pages/follow/users.tsx create mode 100644 pages/interaction/followers.tsx rename pages/{follow/tags.tsx => interaction/following-tags.tsx} (96%) create mode 100644 pages/interaction/following-users.tsx create mode 100644 server/pkg/models/cache.go diff --git a/pages/[username]/index.tsx b/pages/[username]/index.tsx index ed8fe251..b9edcc0f 100644 --- a/pages/[username]/index.tsx +++ b/pages/[username]/index.tsx @@ -1,4 +1,4 @@ -import { Box, chakra, Flex, HStack, VStack, Image, Heading, Text, Button, useColorModeValue, Divider, Wrap, Avatar, Center } from "@chakra-ui/react" +import { Box, chakra, Flex, HStack, VStack, Image, Heading, Text, Button, useColorModeValue, Divider, Wrap, Avatar, Center, useDisclosure, Modal, ModalOverlay, ModalContent, ModalCloseButton, ModalBody, StackDivider, Stack, ModalHeader } from "@chakra-ui/react" import Card from "components/card" import Container from "components/container" import SEO from "components/seo" @@ -7,7 +7,7 @@ import useSession from "hooks/use-session" import PageContainer1 from "layouts/page-container1" import { useRouter } from "next/router" import React, { useEffect, useState } from "react" -import {FaFacebook, FaFile, FaGithub, FaHeart, FaPlus, FaRegStar, FaStackOverflow, FaStar, FaTwitter, FaWeibo, FaZhihu } from "react-icons/fa" +import { FaFacebook, FaFile, FaGithub, FaHeart, FaPlus, FaRegStar, FaStackOverflow, FaStar, FaTwitter, FaWeibo, FaZhihu } from "react-icons/fa" import { ReserveUrls } from "src/data/reserve-urls" import { User } from "src/types/user" import { requestApi } from "utils/axios/request" @@ -18,22 +18,27 @@ import Link from "next/link" import Empty from "components/empty" import Count from "components/count" import { Tag } from "src/types/tag" +import { IDType } from "src/types/id" +import UserCard from "components/users/user-card" +import userCustomTheme from "theme/user-custom" const UserPage = () => { + const { isOpen, onOpen, onClose } = useDisclosure() const router = useRouter() const username = router.query.username const session = useSession() const [user, setUser]: [User, any] = useState(null) const [rawPosts, setRawPosts]: [Story[], any] = useState([]) const [posts, setPosts]: [Story[], any] = useState([]) - const [tags,setTags]:[Tag[],any] = useState([]) - const [tagFilter,setTagFilter]:[Tag,any] = useState(null) - + const [tags, setTags]: [Tag[], any] = useState([]) + const [tagFilter, setTagFilter]: [Tag, any] = useState(null) + const [followers, setFollowers]: [User[], any] = useState([]) const borderColor = useColorModeValue('white', 'transparent') + const stackBorderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark) useEffect(() => { if (username) { initData(username) - + } }, [username]) const initData = async (username) => { @@ -41,7 +46,7 @@ const UserPage = () => { setUser(res.data) getTags(res.data.id) - + const res1 = await requestApi.get(`/user/posts/${res.data.id}`) setPosts(res1.data) setRawPosts(res1.data) @@ -56,16 +61,16 @@ const UserPage = () => { if (tag.id === tagFilter?.id) { setTagFilter(null) setPosts(rawPosts) - return + return } setTagFilter(tag) const p = [] rawPosts.forEach(post => { - for (let i=0;i { setPosts(p) } + const viewFollowers = async tp => { + let res + if (tp === 1) { + // followings + const res0 = await requestApi.get(`/interaction/following/${user.id}?type=${IDType.User}`) + const ids = [] + for (const f of res0.data) { + ids.push(f.id) + } + + + res = await requestApi.post(`/user/ids`, ids) + } else { + // followers + res = await requestApi.get(`/interaction/followers/${user.id}?type=${IDType.User}`) + } + setFollowers(res.data) + if (res.data.length > 0) { + onOpen() + } + } + return ( <> { {user.nickname} {user.tagline && {user.tagline}} - Followers - Following + {/* */} + viewFollowers(0)}>Followers + {/* */} + viewFollowers(1)}>Following - {session?.user.id === user.id ? + {session?.user.id === user.id ? : } - {user.about && - <> - {user.about} - - } + {user.about && + <> + {user.about} + + } {user.location && Location: {user.location} @@ -120,7 +149,7 @@ const UserPage = () => { {moment(user.created).fromNow()} - + {user.github && } {user.twitter && } {user.facebook && } @@ -128,14 +157,14 @@ const UserPage = () => { {user.zhihu && } {user.weibo && } - + {user.availFor && I am available for {user.availFor} } - {user.rawSkills.length > 0 && + {user.rawSkills?.length > 0 && 擅长技能 { @@ -145,7 +174,7 @@ const UserPage = () => { {skill.title} - ) + ) } } @@ -156,7 +185,7 @@ const UserPage = () => { { tags.map(tag => ) } @@ -165,23 +194,36 @@ const UserPage = () => { - - - { posts.length === 0 ? + + + {posts.length === 0 ? : - + - } - - + } + + } + + + + + } > + + {followers.map(user => + + )} + + + + ) diff --git a/pages/follow/followers.tsx b/pages/follow/followers.tsx deleted file mode 100644 index e420d346..00000000 --- a/pages/follow/followers.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import {Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast } from "@chakra-ui/react" -import Card from "components/card" -import Nav from "layouts/nav/nav" -import PageContainer from "layouts/page-container" -import Sidebar from "layouts/sidebar/sidebar" -import React, { useEffect, useState } from "react" -import {adminLinks, followLinks} from "src/data/links" -import { requestApi } from "utils/axios/request" -import TagCard from "components/tags/tag-card" -import { useRouter } from "next/router" -import Link from "next/link" -import { ReserveUrls } from "src/data/reserve-urls" -import { Tag } from "src/types/tag" -import { route } from "next/dist/next-server/server/router" -import PageContainer1 from "layouts/page-container1" -import Empty from "components/empty" - - -const FollowersPage = () => { - const [tags, setTags] = useState([]) - const router = useRouter() - const toast = useToast() - const getTags = () => { - requestApi.get(`/tag/all`).then((res) => setTags(res.data)).catch(_ => setTags([])) - } - - useEffect(() => { - getTags() - }, []) - - const editTag = (tag: Tag) => { - router.push(`${ReserveUrls.Admin}/tag/${tag.name}`) - } - - const deleteTag= async (id) => { - await requestApi.delete(`/tag/${id}`) - getTags() - toast({ - description: "删除成功", - status: "success", - duration: 2000, - isClosable: true, - }) - } - - return ( - <> - - - - - - 标签列表({tags.length}) - - - { - tags.length === 0 ? - - : - <> - - {tags.map(tag => - - editTag(tag)} onDelete={() => deleteTag(tag.id)} /> - - - )} - -
没有更多标签了
- - } -
-
-
- - ) -} -export default FollowersPage - diff --git a/pages/follow/users.tsx b/pages/follow/users.tsx deleted file mode 100644 index 0bcc39da..00000000 --- a/pages/follow/users.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import {Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast } from "@chakra-ui/react" -import Card from "components/card" -import Nav from "layouts/nav/nav" -import PageContainer from "layouts/page-container" -import Sidebar from "layouts/sidebar/sidebar" -import React, { useEffect, useState } from "react" -import {adminLinks, followLinks} from "src/data/links" -import { requestApi } from "utils/axios/request" -import TagCard from "components/tags/tag-card" -import { useRouter } from "next/router" -import Link from "next/link" -import { ReserveUrls } from "src/data/reserve-urls" -import { Tag } from "src/types/tag" -import { route } from "next/dist/next-server/server/router" -import PageContainer1 from "layouts/page-container1" -import Empty from "components/empty" - - -const UsersPage = () => { - const [tags, setTags] = useState([]) - const router = useRouter() - const toast = useToast() - const getTags = () => { - requestApi.get(`/tag/all`).then((res) => setTags(res.data)).catch(_ => setTags([])) - } - - useEffect(() => { - getTags() - }, []) - - const editTag = (tag: Tag) => { - router.push(`${ReserveUrls.Admin}/tag/${tag.name}`) - } - - const deleteTag= async (id) => { - await requestApi.delete(`/tag/${id}`) - getTags() - toast({ - description: "删除成功", - status: "success", - duration: 2000, - isClosable: true, - }) - } - - return ( - <> - - - - - - 标签列表({tags.length}) - - - { - tags.length === 0 ? - - : - <> - - {tags.map(tag => - - editTag(tag)} onDelete={() => deleteTag(tag.id)} /> - - - )} - -
没有更多标签了
- - } -
-
-
- - ) -} -export default UsersPage - diff --git a/pages/interaction/followers.tsx b/pages/interaction/followers.tsx new file mode 100644 index 00000000..1f4ed173 --- /dev/null +++ b/pages/interaction/followers.tsx @@ -0,0 +1,52 @@ +import {Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, Wrap, WrapItem, useColorModeValue, StackDivider } from "@chakra-ui/react" +import Card from "components/card" +import Nav from "layouts/nav/nav" +import PageContainer from "layouts/page-container" +import Sidebar from "layouts/sidebar/sidebar" +import React, { useEffect, useState } from "react" +import {adminLinks, interactionLinks} from "src/data/links" +import { requestApi } from "utils/axios/request" +import PageContainer1 from "layouts/page-container1" +import Empty from "components/empty" +import { IDType } from "src/types/id" +import UserCard from "components/users/user-card" +import userCustomTheme from "theme/user-custom" + + +const FollowersPage = () => { + const [users, setUsers] = useState([]) + const borderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark) + + const getUsers = async () => { + const res = await requestApi.get(`/interaction/followers/0?type=${IDType.User}`) + setUsers(res.data) + } + + useEffect(() => { + getUsers() + }, []) + + return ( + <> + + + + + { + users.length === 0 ? : + } > + + {users.map(user => + + )} + + } + + + + + ) +} +export default FollowersPage + + diff --git a/pages/follow/tags.tsx b/pages/interaction/following-tags.tsx similarity index 96% rename from pages/follow/tags.tsx rename to pages/interaction/following-tags.tsx index e539998f..69c93e69 100644 --- a/pages/follow/tags.tsx +++ b/pages/interaction/following-tags.tsx @@ -2,7 +2,7 @@ import { Text, Box, Heading, Image, Divider, useToast, HStack, Slider, SliderTra import Card from "components/card" import Sidebar from "layouts/sidebar/sidebar" import React, { useEffect, useState } from "react" -import { followLinks } from "src/data/links" +import {interactionLinks} from "src/data/links" import { requestApi } from "utils/axios/request" import { useRouter } from "next/router" import PageContainer1 from "layouts/page-container1" @@ -42,7 +42,7 @@ const TagsPage = () => { <> - + Adjust tag weight to modify your home feed. Higher values mean more appearances. diff --git a/pages/interaction/following-users.tsx b/pages/interaction/following-users.tsx new file mode 100644 index 00000000..0ee491d4 --- /dev/null +++ b/pages/interaction/following-users.tsx @@ -0,0 +1,59 @@ +import {Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, Wrap, WrapItem, useColorModeValue, StackDivider } from "@chakra-ui/react" +import Card from "components/card" +import Nav from "layouts/nav/nav" +import PageContainer from "layouts/page-container" +import Sidebar from "layouts/sidebar/sidebar" +import React, { useEffect, useState } from "react" +import {interactionLinks} from "src/data/links" +import { requestApi } from "utils/axios/request" +import PageContainer1 from "layouts/page-container1" +import Empty from "components/empty" +import { IDType } from "src/types/id" +import UserCard from "components/users/user-card" +import userCustomTheme from "theme/user-custom" + + +const UsersPage = () => { + const [users, setUsers] = useState([]) + const borderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark) + + const getUsers = async () => { + const res = await requestApi.get(`/interaction/following/0?type=${IDType.User}`) + const ids = [] + for (const f of res.data) { + ids.push(f.id) + } + + + const res1 = await requestApi.post(`/user/ids`, ids) + setUsers(res1.data) + } + + useEffect(() => { + getUsers() + }, []) + + return ( + <> + + + + + { + users.length === 0 ? : + } > + + {users.map(user => + + )} + + } + + + + + ) +} +export default UsersPage + + diff --git a/server/internal/api/interaction.go b/server/internal/api/interaction.go index 713d8d9a..7e1c7d1b 100644 --- a/server/internal/api/interaction.go +++ b/server/internal/api/interaction.go @@ -74,13 +74,35 @@ func GetFollowing(c *gin.Context) { userID = u.ID } - tags, err := interaction.GetFollowing(userID, targetType) + following, err := interaction.GetFollowing(userID, targetType) if err != nil { c.JSON(err.Status, common.RespError(err.Message)) return } - c.JSON(http.StatusOK, common.RespSuccess(tags)) + c.JSON(http.StatusOK, common.RespSuccess(following)) +} + +func GetFollowers(c *gin.Context) { + userID := c.Param("userID") + targetType := c.Query("type") + if userID == "" || !models.ValidFollowIDType(targetType) { + c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) + return + } + + if userID == "0" { + u := user.CurrentUser(c) + userID = u.ID + } + + followers, err := interaction.GetFollowers(userID, targetType) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(followers)) } func SetFollowingWeight(c *gin.Context) { diff --git a/server/internal/api/user.go b/server/internal/api/user.go index 4417bce8..0959f646 100644 --- a/server/internal/api/user.go +++ b/server/internal/api/user.go @@ -21,6 +21,19 @@ func GetUsers(c *gin.Context) { c.JSON(http.StatusOK, common.RespSuccess(users)) } +func GetUsersByIDs(c *gin.Context) { + ids := make([]string, 0) + err := c.Bind(&ids) + if err != nil { + c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) + return + } + + us := user.GetUsersByIDs(ids) + + c.JSON(http.StatusOK, common.RespSuccess(us)) +} + func GetUserSelf(c *gin.Context) { u := user.CurrentUser(c) diff --git a/server/internal/cache/cache.go b/server/internal/cache/cache.go index ce6a8d83..76390076 100644 --- a/server/internal/cache/cache.go +++ b/server/internal/cache/cache.go @@ -10,13 +10,12 @@ import ( ) var logger = log.RootLogger.New("logger", "cache") -var Users []*models.User func Init() { - time.Sleep(10 * time.Second) + time.Sleep(2 * time.Second) for { // load users - rows, err := db.Conn.Query(`SELECT id,username,role,nickname,email,avatar,last_seen_at,created FROM user`) + rows, err := db.Conn.Query(`SELECT id,username,role,nickname,avatar,last_seen_at,created FROM user`) if err != nil { logger.Error("load users error", "error", err) time.Sleep(60 * time.Second) @@ -24,9 +23,10 @@ func Init() { } var users []*models.User + usersMap := make(map[string]*models.User) for rows.Next() { user := &models.User{} - err := rows.Scan(&user.ID, &user.Username, &user.Role, &user.Nickname, &user.Email, &user.Avatar, &user.LastSeenAt, &user.Created) + err := rows.Scan(&user.ID, &user.Username, &user.Role, &user.Nickname, &user.Avatar, &user.LastSeenAt, &user.Created) if err != nil { logger.Warn("scan user error", "error", err) continue @@ -43,9 +43,11 @@ func Init() { user.Follows = interaction.GetFollows(user.ID) users = append(users, user) + usersMap[user.ID] = user } - Users = users + models.UsersCache = users + models.UsersMapCache = usersMap time.Sleep(60 * time.Second) } diff --git a/server/internal/interaction/follow.go b/server/internal/interaction/follow.go index 507fdd88..19c2f0cd 100644 --- a/server/internal/interaction/follow.go +++ b/server/internal/interaction/follow.go @@ -93,6 +93,16 @@ func GetFollows(targetID string) int { return follows } +func GetFollowings(userID string, targetType string) int { + var followings int + err := db.Conn.QueryRow("SELECT count(*) FROM follows WHERE user_id=? and target_type=?", userID, targetType).Scan(&followings) + if err != nil && err != sql.ErrNoRows { + logger.Warn("get following count error", "error", err) + } + + return followings +} + func GetFollowing(userID, targetType string) ([]*models.Following, *e.Error) { rows, err := db.Conn.Query("SELECT target_id,weight from follows where user_id=? and target_type=?", userID, targetType) if err != nil { @@ -111,6 +121,28 @@ func GetFollowing(userID, targetType string) ([]*models.Following, *e.Error) { return following, nil } +func GetFollowers(targetID, targetType string) ([]*models.User, *e.Error) { + rows, err := db.Conn.Query("SELECT user_id from follows where target_id=? and target_type=? ORDER BY created DESC", targetID, targetType) + if err != nil { + logger.Warn("get followers error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + users := make([]*models.User, 0) + for rows.Next() { + var id string + rows.Scan(&id) + + u, ok := models.UsersMapCache[id] + if ok { + users = append(users, u) + u.Followed = GetFollowed(u.ID, targetID) + } + } + + return users, nil +} + func SetFolloingWeight(userID string, f *models.Following) *e.Error { _, err := db.Conn.Exec("UPDATE follows SET weight=? WHERE user_id=? and target_id=?", f.Weight, userID, f.ID) if err != nil { diff --git a/server/internal/search/search.go b/server/internal/search/search.go index f4d626b4..d06e762c 100644 --- a/server/internal/search/search.go +++ b/server/internal/search/search.go @@ -4,7 +4,6 @@ import ( "sort" "strings" - "github.com/imdotdev/im.dev/server/internal/cache" "github.com/imdotdev/im.dev/server/internal/interaction" "github.com/imdotdev/im.dev/server/internal/story" "github.com/imdotdev/im.dev/server/pkg/db" @@ -39,10 +38,8 @@ func Posts(user *models.User, filter, query string) []*models.Story { } func Users(user *models.User, filter, query string) []*models.User { - allUsers := cache.Users - users := make(models.Users, 0) - for _, u := range allUsers { + for _, u := range models.UsersCache { if strings.Contains(strings.ToLower(u.Nickname), strings.ToLower(query)) { users = append(users, u) continue diff --git a/server/internal/server.go b/server/internal/server.go index bebb0ce3..9c34333a 100644 --- a/server/internal/server.go +++ b/server/internal/server.go @@ -75,6 +75,7 @@ func (s *Server) Start() error { r.GET("/tag/user/:userID", api.GetUserTags) // user apis r.GET("/user/all", api.GetUsers) + r.POST("/user/ids", api.GetUsersByIDs) r.GET("/user/self", IsLogin(), api.GetUserSelf) r.GET("/user/info/:username", api.GetUser) r.POST("/user/update", IsLogin(), api.UpdateUser) @@ -89,6 +90,7 @@ func (s *Server) Start() error { r.POST("/interaction/following/weight", IsLogin(), api.SetFollowingWeight) r.GET("/interaction/followed/:id", api.Followed) r.GET("/interaction/following/:userID", api.GetFollowing) + r.GET("/interaction/followers/:userID", api.GetFollowers) // search apis r.GET("/search/posts/:filter", api.SearchPosts) diff --git a/server/internal/user/users.go b/server/internal/user/users.go index 16a6fcff..8a689117 100644 --- a/server/internal/user/users.go +++ b/server/internal/user/users.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/imdotdev/im.dev/server/internal/cache" "github.com/imdotdev/im.dev/server/internal/interaction" "github.com/imdotdev/im.dev/server/internal/tags" "github.com/imdotdev/im.dev/server/pkg/db" @@ -15,10 +14,8 @@ import ( ) func GetUsers(q string) ([]*models.User, *e.Error) { - allUsers := cache.Users - users := make([]*models.User, 0) - for _, u := range allUsers { + for _, u := range models.UsersCache { if strings.HasPrefix(strings.ToLower(u.Nickname), strings.ToLower(q)) { users = append(users, u) continue @@ -33,6 +30,19 @@ func GetUsers(q string) ([]*models.User, *e.Error) { return users, nil } +func GetUsersByIDs(ids []string) []*models.User { + users := make([]*models.User, 0) + for _, id := range ids { + u, ok := models.UsersMapCache[id] + if ok { + users = append(users, u) + } + u.Followed = true + } + + return users +} + func GetUserDetail(id string, username string) (*models.User, *e.Error) { user := &models.User{} err := user.Query(id, username, "") @@ -65,6 +75,8 @@ func GetUserDetail(id string, username string) (*models.User, *e.Error) { user.Follows = interaction.GetFollows(user.ID) + user.Followings = interaction.GetFollowings(user.ID, models.IDTypeUser) + return user, nil } diff --git a/server/pkg/common/reserve_urls.go b/server/pkg/common/reserve_urls.go index 4f9b5cb0..705e20e7 100644 --- a/server/pkg/common/reserve_urls.go +++ b/server/pkg/common/reserve_urls.go @@ -4,7 +4,7 @@ package common var ReserverURLs = []string{ "/tags", "/courses", - "/follow", + "/interaction", "/editor", "/admin", "/bookmarks", diff --git a/server/pkg/models/cache.go b/server/pkg/models/cache.go new file mode 100644 index 00000000..b8214bee --- /dev/null +++ b/server/pkg/models/cache.go @@ -0,0 +1,4 @@ +package models + +var UsersCache []*User +var UsersMapCache map[string]*User diff --git a/server/pkg/models/user.go b/server/pkg/models/user.go index 772c68c6..6c546b18 100644 --- a/server/pkg/models/user.go +++ b/server/pkg/models/user.go @@ -11,27 +11,28 @@ type User struct { Username string `json:"username"` Nickname string `json:"nickname"` Avatar string `json:"avatar"` - Email string `json:"email"` - Role RoleType `json:"role"` - - Tagline string `json:"tagline"` - Cover string `json:"cover"` - Location string `json:"location"` - AvailFor string `json:"availFor"` - About string `json:"about"` - RawSkills []*Tag `json:"rawSkills"` - Skills []string `json:"skills"` - - Website string `json:"website"` - Twitter string `json:"twitter"` - Github string `json:"github"` - Zhihu string `json:"zhihu"` - Weibo string `json:"weibo"` - Facebook string `json:"facebook"` - Stackoverflow string `json:"stackoverflow"` - - Follows int `json:"follows"` - Followed bool `json:"followed"` + Email string `json:"email,omitempty"` + Role RoleType `json:"role,omitempty"` + + Tagline string `json:"tagline,omitempty"` + Cover string `json:"cover,omitempty"` + Location string `json:"location,omitempty"` + AvailFor string `json:"availFor,omitempty"` + About string `json:"about,omitempty"` + RawSkills []*Tag `json:"rawSkills,omitempty"` + Skills []string `json:"skills,omitempty"` + + Website string `json:"website,omitempty"` + Twitter string `json:"twitter,omitempty"` + Github string `json:"github,omitempty"` + Zhihu string `json:"zhihu,omitempty"` + Weibo string `json:"weibo,omitempty"` + Facebook string `json:"facebook,omitempty"` + Stackoverflow string `json:"stackoverflow,omitempty"` + + Follows int `json:"follows,omitempty"` + Followings int `json:"followings,omitempty"` + Followed bool `json:"followed,omitempty"` LastSeenAt time.Time `json:"lastSeenAt,omitempty"` Created time.Time `json:"created"` diff --git a/src/components/card.tsx b/src/components/card.tsx index 396f65e4..d510dc4e 100644 --- a/src/components/card.tsx +++ b/src/components/card.tsx @@ -14,6 +14,7 @@ export const Card = ({shadowed, ...rest}: BoxProps&Props) => { borderWidth="1px" p={[2,2,4,4]} boxShadow={shadowed? "0 1px 1px 0 rgb(0 0 0 / 5%)" : null} + height="fit-content" {...rest} /> ) diff --git a/src/components/user-menu.tsx b/src/components/user-menu.tsx index 95e0c35d..d6f6022f 100644 --- a/src/components/user-menu.tsx +++ b/src/components/user-menu.tsx @@ -57,7 +57,7 @@ export const UserMenu = () => { {isEditor(session.user.role) && } >创作中心} }>书签收藏 - }>我的关注 + }>我的关注 {isAdmin(session.user.role) && } >管理员} }>偏好设置 diff --git a/src/components/users/user-card.tsx b/src/components/users/user-card.tsx index 30af0756..f05b54d7 100644 --- a/src/components/users/user-card.tsx +++ b/src/components/users/user-card.tsx @@ -7,6 +7,7 @@ import { User } from "src/types/user" import { getUserName } from "utils/user" import Follow from "components/interaction/follow" import Highlighter from 'react-highlight-words'; +import Count from "components/count" type Props = PropsOf & { user : User @@ -45,7 +46,7 @@ export const UserCard= ({user,highlight}:Props) =>{ - {user.follows} followers + followers diff --git a/src/data/links.tsx b/src/data/links.tsx index 2e2d082b..464780c4 100644 --- a/src/data/links.tsx +++ b/src/data/links.tsx @@ -24,20 +24,20 @@ export const editorLinks: Route[] = [{ } ] -export const followLinks: any[] = [ +export const interactionLinks: any[] = [ { title: 'Following tags', - path: `${ReserveUrls.Follow}/tags`, + path: `${ReserveUrls.Interaction}/following-tags`, disabled: false }, { title: 'Following users', - path: `${ReserveUrls.Follow}/users`, + path: `${ReserveUrls.Interaction}/following-users`, disabled: false }, { title: 'Followers', - path: `${ReserveUrls.Follow}/followers`, + path: `${ReserveUrls.Interaction}/followers`, disabled: false }, ] diff --git a/src/data/reserve-urls.ts b/src/data/reserve-urls.ts index 8f721029..1b75b83a 100644 --- a/src/data/reserve-urls.ts +++ b/src/data/reserve-urls.ts @@ -2,7 +2,7 @@ export enum ReserveUrls { Tags = "/tags", Courses = "/courses", - Follow = "/follow", + Interaction = "/interaction", Editor = "/editor", Admin = "/admin", Bookmarks = "/bookmarks", diff --git a/src/types/user.ts b/src/types/user.ts index 9d1a630f..a8fba0ea 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -32,7 +32,8 @@ export interface User { facebook?: string stackoverflow?: string - follows?: number + follows?: number + followings?: number followed?: boolean lastSeenAt?: string