From c959e0945b67c296ac6b09e0f5b6d7cbfee11ae4 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Wed, 27 Sep 2023 21:57:28 +0200 Subject: [PATCH 01/33] feat(): Recommender Service for People Discovery with corresponding views --- src/AccountProfile.jsx | 76 ++++- src/AccountProfileOverlay.jsx | 73 ++++- src/LatestPeople.jsx | 96 +++++-- src/PeoplePage.jsx | 84 ++++-- .../Account/AccountProfileLargeCard.jsx | 261 ++++++++++++++++++ .../Account/AccountProfileViewSwitch.jsx | 44 +++ src/Recommender/Service/RecommendedUsers.jsx | 179 ++++++++++++ .../Views/FriendsOfFriendsView.jsx | 138 +++++++++ src/Recommender/Views/RecommendedAvatars.jsx | 52 ++++ src/Recommender/Views/TrendingUsersView.jsx | 23 ++ src/Select.jsx | 4 +- 11 files changed, 965 insertions(+), 65 deletions(-) create mode 100644 src/Recommender/Account/AccountProfileLargeCard.jsx create mode 100644 src/Recommender/Account/AccountProfileViewSwitch.jsx create mode 100644 src/Recommender/Service/RecommendedUsers.jsx create mode 100644 src/Recommender/Views/FriendsOfFriendsView.jsx create mode 100644 src/Recommender/Views/RecommendedAvatars.jsx create mode 100644 src/Recommender/Views/TrendingUsersView.jsx diff --git a/src/AccountProfile.jsx b/src/AccountProfile.jsx index 9d009d75..d6bd8095 100644 --- a/src/AccountProfile.jsx +++ b/src/AccountProfile.jsx @@ -22,14 +22,39 @@ const Wrapper = styled.a` min-width: 0; } + .hover-state1 { + opacity: 0; + } + + .hover-state2 { + opacity: 1; + } + &:hover, &:focus { - div:first-child { + div.hover { border-color: #d0d5dd; } + .hover-state1 { + opacity: 1; + transition: opacity 0.3s ease; + } + .hover-state2 { + opacity: 0; + transition: opacity 0.3s ease; + } } `; +const AvatarCount = styled.div` + color: rgb(104, 112, 118); + font-size: 14px; + font-weight: 300; + position: absolute; + right: 0px; + padding-right: 20px; +`; + const Text = styled.p` margin: 0; font-size: 14px; @@ -70,7 +95,7 @@ const AccountProfile = ( href={!props.onClick && profileUrl} onClick={props.onClick && (() => props.onClick(accountId))} > - + Joined{" "} {" "} ago )} + + {props.scope == "friends" && props.becauseYouFollow.length > 0 && ( + <> + + + + + {props.becauseYouFollow.length} friends follow + + + )} + + {props.scope == "similar" && ( + Shared interests + )} {!props.hideAccountId && @{accountId}} @@ -110,13 +156,19 @@ const AccountProfile = ( if (props.noOverlay) return AccountProfile; return ( - + <> + + ); diff --git a/src/AccountProfileOverlay.jsx b/src/AccountProfileOverlay.jsx index a0668831..7983a561 100644 --- a/src/AccountProfileOverlay.jsx +++ b/src/AccountProfileOverlay.jsx @@ -20,6 +20,24 @@ const CardWrapper = styled.div` padding: 6px; `; +const OverlayTagsWrapper = styled.div` + margin-left: 50px; +`; + +const AvatarCount = styled.div` + color: rgb(104, 112, 118); + font-size: 14px; + font-weight: 300; + padding-left: 12px; +`; + +const RecommendedAvatars = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 12px 0 0; +`; + const Card = styled.div` display: flex; flex-direction: column; @@ -32,12 +50,16 @@ const Card = styled.div` border: 1px solid #eceef0; box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); padding: 12px; + + div.layout { + margin-left: 63px; + } `; const FollowButtonWrapper = styled.div` width: 100%; - - div, button { + div, + button { width: 100%; } `; @@ -48,6 +70,12 @@ const contentModerationItem = { reportedBy: context.accountId, }; +const handleFollow = (userId) => { + console.log("remove from list, because followed:", userId); + // callback function + // removeListProfileOnFollow(userId); +}; + const overlay = ( @@ -69,15 +97,41 @@ const overlay = ( )} - + {props.scope === "friends" ? ( + + + + + + {props.becauseYouFollow.length} friends following + + + + ) : ( + + )} + {!!context.accountId && context.accountId !== props.accountId && ( )} @@ -110,12 +164,13 @@ return ( props={{ type: "info", title: "Flagged for moderation", - description: "Thanks for helping our Content Moderators. The item you flagged will be reviewed.", + description: + "Thanks for helping our Content Moderators. The item you flagged will be reviewed.", open: state.hasBeenFlagged, onOpenChange: (open) => { State.update({ hasBeenFlagged: open }); }, - duration: 10000 + duration: 10000, }} /> )} diff --git a/src/LatestPeople.jsx b/src/LatestPeople.jsx index e2e51f72..324decf1 100644 --- a/src/LatestPeople.jsx +++ b/src/LatestPeople.jsx @@ -23,6 +23,26 @@ let accounts = Object.entries(accountsWithProfileData || {}) accounts.reverse(); +State.init({ + selectedView: { text: "Top", value: "trending" }, +}); + +const handleViewChange = (option) => { + State.update({ selectedView: { value: option.value } }); +}; + +const options = [ + { text: "Latest", value: "latest" }, + { text: "Top", value: "trending" }, + { text: "Recommended", value: "recommended" }, +]; + +const FlexContainer = styled.div` + display: flex; + align-items: center; + justify-content: space-between; +`; + const Wrapper = styled.div` display: grid; gap: 24px; @@ -35,7 +55,7 @@ const H2 = styled.h2` margin: 0; `; -const Items = styled.div` +const LatestPeople = styled.div` display: grid; gap: 18px; `; @@ -70,26 +90,62 @@ const ButtonLink = styled.a` } `; +const Select = styled.div` + max-width: 100%; +`; + return ( -

People

- - - {accounts.map((account) => ( - - - - ))} - - - - View All People ({totalAccounts}) - + +

People

+ +
+ {state.selectedView.value === "latest" && ( + <> + + {accounts.map((account) => ( + + + + ))} + + + View All People ({totalAccounts}) + + + )} + {state.selectedView.value === "trending" && ( + <> + + + )} + {state.selectedView.value === "recommended" && ( + <> + + + )}
); diff --git a/src/PeoplePage.jsx b/src/PeoplePage.jsx index cadfb420..64ef2f8d 100644 --- a/src/PeoplePage.jsx +++ b/src/PeoplePage.jsx @@ -46,7 +46,9 @@ if (data) { if ( state.selectedTab === "everyone" || (state.selectedTab === "following" && isFollowing) || - (state.selectedTab === "followers" && isFollower) + (state.selectedTab === "followers" && isFollower) || + state.selectedTab === "trending" || + state.selectedTab === "recommended" ) { result.push({ accountId, @@ -275,6 +277,20 @@ return ( Followers )} + + Trending + + {context.accountId && ( + + Recommended + + )} )} @@ -282,29 +298,53 @@ return ( No people matched your search. )} - {items.length > 0 && ( - - {items.map((person, i) => ( - - - - ))} - + {(state.selectedTab == "everyone" || + state.selectedTab == "following" || + state.selectedTab == "followers") && + items.length > 0 && ( + + {items.map((person, i) => ( + + + + ))} + + )} + + {!context.accountId && state.selectedTab == "trending" && ( + )} - {showLoadMoreButton && ( - + {context.accountId && state.selectedTab == "trending" && ( + + )} + + {context.accountId && state.selectedTab == "recommended" && ( + )} + + {(state.selectedTab == "everyone" || + state.selectedTab == "following" || + state.selectedTab == "followers") && + showLoadMoreButton && ( + + )} ); diff --git a/src/Recommender/Account/AccountProfileLargeCard.jsx b/src/Recommender/Account/AccountProfileLargeCard.jsx new file mode 100644 index 00000000..03da4ad7 --- /dev/null +++ b/src/Recommender/Account/AccountProfileLargeCard.jsx @@ -0,0 +1,261 @@ +const accountId = props.accountId; +const profile = props.profile || Social.get(`${accountId}/profile/**`, "final"); +const tags = Object.keys(profile.tags || {}); +const profileUrl = `#/near/widget/ProfilePage?accountId=${accountId}`; + +const abbreviateNumber = (value) => { + let newValue = value; + if (value >= 1000) { + const suffixes = ["", "k", "M"]; + let suffixNum = 0; + if (value < 1000000) { + suffixNum = 1; // Use 'k' + } else if (value < 1000000000) { + suffixNum = 2; // Use 'M' + } + const shortValue = (value / Math.pow(1000, suffixNum)).toFixed(1); + newValue = shortValue + suffixes[suffixNum]; + } + return newValue; +}; + +const Avatar = styled.a` + width: 60px; + height: 60px; + flex-shrink: 0; + border: 1px solid #eceef0; + overflow: hidden; + border-radius: 56px; + transition: border-color 200ms; + + img { + object-fit: cover; + width: 100%; + height: 100%; + } + + &:hover, + &:focus { + border-color: #d0d5dd; + } +`; + +const Button = styled.div` + div > .follow-button { + color: #000 !important; + } +`; + +const CurrentUserProfile = styled.div` + height: 32px; +`; + +const NoTags = styled.div` + height: 20px; +`; + +const Score = styled.li` + font-size: 12px; + font-weight: 500; + color: #90908c; + i { + font-size: 12px; + font-weight: 900; + color: #90908c; + } +`; + +const Scores = styled.ul` + display: flex; + justify-content: space-around; + align-items: left; + width: 100%; + margin-bottom: 0px; + padding: 0px 40px 14px; + border-bottom: 1px solid #eceef0; + color: #90908c; + list-style-type: none; + min-height: 32px; +`; + +const TagsWrapper = styled.div` + max-width: 80%; + margin-top: -5px; +`; + +const TextLink = styled.a` + display: block; + margin: 0; + font-size: 14px; + line-height: 18px; + color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; + font-weight: ${(p) => (p.bold ? "600" : "400")}; + font-size: ${(p) => (p.small ? "12px" : "14px")}; + overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; + text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; + white-space: nowrap; + outline: none; + max-width: 230px; + + &:focus, + &:hover { + text-decoration: underline; + } +`; + +const ProfileListContainer = styled.div` + width: auto; + position: relative; + font-size: 14px; + color: #90908c; +`; + +const ProfileList = styled.ul` + list-style: none; + padding: 0; + margin: 0; + height: 100%; + font-size: 14px; + color: #90908c; +`; + +const Profile = styled.li` + padding: 0px; +`; + +const FollowsYouBadge = styled.p` + display: inline-block; + margin: 0px; + font-size: 10px; + line-height: 1.1rem; + background: rgb(104, 112, 118); + color: rgb(255, 255, 255); + font-weight: 600; + white-space: nowrap; + padding: 2px 6px; + border-radius: 3px; +`; + +const LargeCard = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 12px; + width: 100%; + border-radius: 12px; + z-index: 1070; + background: #fff; + border: 1px solid #eceef0; + box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), + 0px 1px 2px rgba(16, 24, 40, 0.06); + overflow: hidden; + padding: 24px 0px 13px; +`; + +const CenteredLinksWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + padding-top: 14px; +`; +return ( + + + + + + + {props.profileName || profile.name || accountId.split(".near")[0]} + + + @{accountId} + + + {tags.length > 0 ? ( + + + + ) : ( + + + + )} + + {props.following !== null && + props.followers !== null && + props.likers !== null ? ( + + {props.followers > 0 && ( + + Followers} + > +
+ + {abbreviateNumber(props.followers)} +
+
+
+ )} + {props.following > 0 && ( + + Following} + > +
+ + {abbreviateNumber(props.following)} +
+
+
+ )} + {props.likers > 0 && ( + + Likes received} + > +
+ + {abbreviateNumber(props.likers)} +
+
+
+ )} +
+ ) : ( + + + + )} + + {context.accountId && context.accountId !== props.accountId ? ( + + ) : ( + + )} +
+); diff --git a/src/Recommender/Account/AccountProfileViewSwitch.jsx b/src/Recommender/Account/AccountProfileViewSwitch.jsx new file mode 100644 index 00000000..3a1c72d9 --- /dev/null +++ b/src/Recommender/Account/AccountProfileViewSwitch.jsx @@ -0,0 +1,44 @@ +const accountId = props.accountId; +const profile = props.profile || Social.get(`${accountId}/profile/**`, "final"); +const tags = Object.keys(profile.tags || {}); +const profileUrl = `#/near/widget/ProfilePage?accountId=${accountId}`; + +return ( + <> + {props.sidebar ? ( + + ) : ( + + )} + +); diff --git a/src/Recommender/Service/RecommendedUsers.jsx b/src/Recommender/Service/RecommendedUsers.jsx new file mode 100644 index 00000000..fe0c0e6e --- /dev/null +++ b/src/Recommender/Service/RecommendedUsers.jsx @@ -0,0 +1,179 @@ +const Button = styled.button` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 36px 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + +const H1 = styled.h1` + font-weight: 600; + font-size: 64px; + line-height: normal; + color: #11181c; + margin: 4rem 0; +`; + +const Profile = styled.div``; + +const Profiles = styled.div` + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 24px; + + @media (max-width: 1024px) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + @media (max-width: 800px) { + grid-template-columns: minmax(0, 1fr); + } +`; + +const RecommendedUsers = styled.div` + display: flex; + flex-direction: column; + gap: 12px; + padding-bottom: ${props.sidebar ? "12px" : "50px"}; +`; + +State.init({ + currentPage: 1, + userData: [], + isLoading: true, + error: null, + totalPages: 1, +}); + +const updateState = (data, totalPageNum) => { + State.update({ + isLoading: false, + userData: [...state.userData, ...data], + totalPages: totalPageNum, + }); +}; + +const getRecommendedUsers = (page) => { + try { + const url = `${props.dataset}_${page}.json`; + if (state.currentPage == 1) { + const res = fetch(url); + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log( + "Error fetching data. Try reloading the page, or no data available." + ); + } + } else { + asyncFetch(url).then((res) => { + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log("Error fetching data. Try reloading the page."); + } + }); + } + } catch (error) { + console.log(error.message); + } +}; + +const loadMore = () => { + const nextPage = state.currentPage + 1; + if (nextPage <= state.totalPages) { + State.update({ currentPage: nextPage }); + getRecommendedUsers(nextPage); + } +}; + +const removeListProfileOnFollow = (rank) => { + const updatedUserData = userData.filter((rank) => rank !== rank); + State.update({ userData: [updatedUserData] }); +}; + +if (state.isLoading) { + console.log("state:", state.isLoading); + getRecommendedUsers(state.currentPage); +} + +const returnElements = props.returnElements; +const displayedUsers = returnElements + ? state.userData.slice(0, returnElements) + : state.userData; + +return ( + + {state.isLoading && displayedUsers.length == null &&

Loading...

} + {state.error &&

Error: {state.error}

} + {(displayedUsers.length < 4 || displayedUsers == null) && + state.isLoading && + (props.scope == "friends" || props.scope === "similar") && ( +

+ Follow More Users to Unlock More Personalized Recommendations, See + Who’s{" "} + + Trending + +

+ )} + + {displayedUsers.map((user, index) => ( + + + + ))} + + {!props.returnElements && state.currentPage < state.totalPages ? ( + + ) : null} +
+); diff --git a/src/Recommender/Views/FriendsOfFriendsView.jsx b/src/Recommender/Views/FriendsOfFriendsView.jsx new file mode 100644 index 00000000..f15f2bcd --- /dev/null +++ b/src/Recommender/Views/FriendsOfFriendsView.jsx @@ -0,0 +1,138 @@ +const CategoryHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const RecommendationsView = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + +const H3 = styled.h3` + font-weight: 600; + font-size: 24px; + line-height: normal; + color: #11181c; + margin: 1.8rem 0 1.5rem 0; +`; + +const Profile = styled.div``; + +const Profiles = styled.div` + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 24px; + + @media (max-width: 1024px) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + @media (max-width: 800px) { + grid-template-columns: minmax(0, 1fr); + } +`; + +const RetroLinkButton = styled.button` + background: none; + border: none; + cursor: pointer; + color: blue; + font-size: 16px; + padding: 0; + margin: 0; + outline: none; + transition: color 0.3s; + + &:hover { + color: purple; + } + + &:active { + color: red; + } +`; + +State.init({ + currentPage: 1, + userData: [], + isLoading: true, + error: null, + totalPages: 1, + expandedList: "", +}); + +const handleToggleList = (listId) => { + if (state.expandedList === listId) { + State.update({ expandedList: "" }); + } else { + State.update({ expandedList: listId }); + } +}; + +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; +const recommendedProfiles = "second_degree_following"; +const similarProfiles = "similarity_estimation"; + +const recommendedProfilesURL = `${BASE_URL}/${recommendedProfiles}_${context.accountId}`; +const similarProfilesURL = `${BASE_URL}/${similarProfiles}_${context.accountId}`; + +return ( + + {state.expandedList !== "list2" && ( + <> + {props.sidebar ? ( + <> + ) : ( + +

Friends of Friends

+ handleToggleList("list1")}> + {state.expandedList === "list1" + ? "Back to categories" + : "View all"} + +
+ )} + + + )} + + {state.expandedList !== "list1" && ( + <> + {props.sidebar ? ( + <> + ) : ( + +

Similar to you

+ handleToggleList("list2")}> + {state.expandedList === "list2" + ? "Back to categories" + : "View all"} + +
+ )} + + + )} +
+); diff --git a/src/Recommender/Views/RecommendedAvatars.jsx b/src/Recommender/Views/RecommendedAvatars.jsx new file mode 100644 index 00000000..26b992df --- /dev/null +++ b/src/Recommender/Views/RecommendedAvatars.jsx @@ -0,0 +1,52 @@ +const Avatar = styled.div` + width: ${props.avatarSize || "40px"}; + height: ${props.avatarSize || "40px"}; + flex-shrink: 0; + border: 1px solid #eceef0; + overflow: hidden; + border-radius: ${props.avatarSize || "40px"}; + + img { + object-fit: cover; + width: 100%; + height: 100%; + }uu +`; + +const AvatarContainer = styled.div` + display: flex; + padding-right: 7px; + + & > div { + margin-right: -8px; + border: 2px solid white; + } + & > div:nth-child(n + 4) { + display: none; + } +`; + +const profiles = props.becauseYouFollow; + +return ( + <> + {profiles ? ( + + {profiles.map((avatar, index) => ( + + + {avatar} + + ))} + + ) : null} + +); diff --git a/src/Recommender/Views/TrendingUsersView.jsx b/src/Recommender/Views/TrendingUsersView.jsx new file mode 100644 index 00000000..9cca5535 --- /dev/null +++ b/src/Recommender/Views/TrendingUsersView.jsx @@ -0,0 +1,23 @@ +const TrendingUsersView = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; +const algorithm = "trending_users"; +const trendingProfilesURL = `${BASE_URL}/${algorithm}_page`; + +return ( + + + +); diff --git a/src/Select.jsx b/src/Select.jsx index f0ad9789..db77d6c5 100644 --- a/src/Select.jsx +++ b/src/Select.jsx @@ -50,8 +50,8 @@ const Input = styled.div` padding: 0.5em 0.75em; gap: 0.5em; background: #ffffff; - border: 1px solid #d0d5dd; - box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); + border: ${props.border || "1px solid #d0d5dd"}; + box-shadow: ${props.border || "0px 1px 2px rgba(16, 24, 40, 0.05)"}; border-radius: 4px; color: #101828; width: 100%; From 664039a7d84ea818b949eb3dda82a89e73e4eac4 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Wed, 27 Sep 2023 21:57:59 +0200 Subject: [PATCH 02/33] feat() Engagement Tracker Service - WIP --- .../Engagement/FollowButtonTracker.jsx | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 src/Recommender/Engagement/FollowButtonTracker.jsx diff --git a/src/Recommender/Engagement/FollowButtonTracker.jsx b/src/Recommender/Engagement/FollowButtonTracker.jsx new file mode 100644 index 00000000..7d6c9279 --- /dev/null +++ b/src/Recommender/Engagement/FollowButtonTracker.jsx @@ -0,0 +1,168 @@ +///use: +// { { +// trackEngagement({ +// targetSigner: props.accountId, +// rank: props.rank, +// eventType: "people page avatar profile entry", +// }); +// }} +// href={profileUrl} +// > +// +// +// { +// trackEngagement({ +// targetSigner: props.accountId, +// rank: props.rank, +// eventType: "people page profile entry", +// }); +// }} +// > +// +// {props.profileName || profile.name || accountId.split(".near")[0]} +// +// +// @{accountId} +// +// + +// } + +/////////////// +const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test +// const dataplane = "https://near.dataplane.rudderstack.com"; //prod +const uri = "/v1/track"; +const api_url = `${dataplane}${uri}`; +const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test +// const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod +const currentTimeStamp = new Date().toISOString(); + +const trackEngagement = (targetSigner, eventType, rank) => { + const payload = { + userId: context.accountId, + event: eventType, + properties: { + targetSignerId: targetSigner, + rank: rank, + }, + timestamp: new Date().toISOString(), + }; + + asyncFetch(api_url, { + body: JSON.stringify({ payload }), + headers: { + "Content-Type": "application/json", + Authorization: auth, + }, + method: "POST", + }) + .then((response) => response.json()) + .then((data) => console.log(data)) + .catch((error) => console.log("Error:", error)); +}; +//////////// + +if ( + !props.accountId || + !context.accountId || + context.accountId === props.accountId +) { + return ""; +} + +const followEdge = Social.keys( + `${context.accountId}/graph/follow/${props.accountId}`, + undefined, + { + values_only: true, + } +); + +const inverseEdge = Social.keys( + `${props.accountId}/graph/follow/${context.accountId}`, + undefined, + { + values_only: true, + } +); + +const loading = followEdge === null || inverseEdge === null; +const isFollowing = Object.keys(followEdge || {}).length > 0; +const isInverse = Object.keys(inverseEdge || {}).length > 0; + +const type = follow ? "unfollow" : "follow"; + +const data = { + graph: { follow: { [props.accountId]: isFollowing ? null : "" } }, + index: { + graph: JSON.stringify({ + key: "follow", + value: { + type, + accountId: props.accountId, + }, + }), + notify: JSON.stringify({ + key: props.accountId, + value: { + type, + }, + }), + }, +}; + +const Wrapper = styled.div` + .follow-button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px 16px; + height: 32px; + border-radius: 100px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + background: #fbfcfd; + //background: red; + border: 1px solid #d7dbdf; + color: #006adc !important; + white-space: nowrap; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + i { + color: #7e868c; + } + + .bi-16 { + font-size: 16px; + } + } +`; + +return ( + + + {isFollowing && } + {isFollowing ? "Following" : isInverse ? "Follow Back" : "Follow"} + + +); From 6cf435a3740c8d5e10a122caa6c5b55e0159f356 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Thu, 28 Sep 2023 10:47:51 +0200 Subject: [PATCH 03/33] feat() Get avatar images from Social DB instead of API in RecommendedAvatar module --- src/Recommender/Views/RecommendedAvatars.jsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Recommender/Views/RecommendedAvatars.jsx b/src/Recommender/Views/RecommendedAvatars.jsx index 26b992df..0631888f 100644 --- a/src/Recommender/Views/RecommendedAvatars.jsx +++ b/src/Recommender/Views/RecommendedAvatars.jsx @@ -1,16 +1,18 @@ const Avatar = styled.div` + display: flex; width: ${props.avatarSize || "40px"}; height: ${props.avatarSize || "40px"}; flex-shrink: 0; border: 1px solid #eceef0; overflow: hidden; border-radius: ${props.avatarSize || "40px"}; + background-color: white; img { object-fit: cover; width: 100%; height: 100%; - }uu + } `; const AvatarContainer = styled.div` @@ -27,12 +29,18 @@ const AvatarContainer = styled.div` `; const profiles = props.becauseYouFollow; +const account = Social.get(`${accountId}/profile/**`, "final"); +const fourProfiles = profiles.slice(0, 4); + +const avatarData = fourProfiles.map((profile) => { + return Social.get(`${profile}/profile/**`, "final"); +}); return ( <> {profiles ? ( - {profiles.map((avatar, index) => ( + {avatarData.map((avatar, index) => ( - {avatar} ))} From ad628763fb74b9d139383eb059c318f51745413d Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Fri, 29 Sep 2023 12:49:53 +0200 Subject: [PATCH 04/33] feat() cleanup FoF view and route to fixed url, along with cleanup other components --- src/AccountProfile.jsx | 2 -- src/AccountProfileOverlay.jsx | 7 ------ .../Account/AccountProfileViewSwitch.jsx | 3 +-- .../Views/FriendsOfFriendsView.jsx | 24 +------------------ 4 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/AccountProfile.jsx b/src/AccountProfile.jsx index d6bd8095..847debfc 100644 --- a/src/AccountProfile.jsx +++ b/src/AccountProfile.jsx @@ -156,7 +156,6 @@ const AccountProfile = ( if (props.noOverlay) return AccountProfile; return ( - <> - ); diff --git a/src/AccountProfileOverlay.jsx b/src/AccountProfileOverlay.jsx index 7983a561..6f87a266 100644 --- a/src/AccountProfileOverlay.jsx +++ b/src/AccountProfileOverlay.jsx @@ -70,12 +70,6 @@ const contentModerationItem = { reportedBy: context.accountId, }; -const handleFollow = (userId) => { - console.log("remove from list, because followed:", userId); - // callback function - // removeListProfileOnFollow(userId); -}; - const overlay = ( @@ -130,7 +124,6 @@ const overlay = ( src="${REPL_ACCOUNT}/widget/FollowButton" props={{ accountId: props.accountId, - onClick: handleFollow(props.accountId), }} /> diff --git a/src/Recommender/Account/AccountProfileViewSwitch.jsx b/src/Recommender/Account/AccountProfileViewSwitch.jsx index 3a1c72d9..b152c746 100644 --- a/src/Recommender/Account/AccountProfileViewSwitch.jsx +++ b/src/Recommender/Account/AccountProfileViewSwitch.jsx @@ -19,8 +19,7 @@ return ( profileImage: props.profileImage || null, profileName: props.profileName || null, scope: props.scope || null, - overlayPlacement: "auto", //"left", - onFollow: { removeListProfileOnFollow }, + overlayPlacement: "left", }} /> ) : ( diff --git a/src/Recommender/Views/FriendsOfFriendsView.jsx b/src/Recommender/Views/FriendsOfFriendsView.jsx index f15f2bcd..be3874f9 100644 --- a/src/Recommender/Views/FriendsOfFriendsView.jsx +++ b/src/Recommender/Views/FriendsOfFriendsView.jsx @@ -18,22 +18,6 @@ const H3 = styled.h3` margin: 1.8rem 0 1.5rem 0; `; -const Profile = styled.div``; - -const Profiles = styled.div` - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 24px; - - @media (max-width: 1024px) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - @media (max-width: 800px) { - grid-template-columns: minmax(0, 1fr); - } -`; - const RetroLinkButton = styled.button` background: none; border: none; @@ -55,11 +39,6 @@ const RetroLinkButton = styled.button` `; State.init({ - currentPage: 1, - userData: [], - isLoading: true, - error: null, - totalPages: 1, expandedList: "", }); @@ -74,7 +53,7 @@ const handleToggleList = (listId) => { const STORE = "storage.googleapis.com"; const BUCKET = "databricks-near-query-runner"; const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; -const recommendedProfiles = "second_degree_following"; +const recommendedProfiles = "friends_of_friends"; const similarProfiles = "similarity_estimation"; const recommendedProfilesURL = `${BASE_URL}/${recommendedProfiles}_${context.accountId}`; @@ -129,7 +108,6 @@ return ( returnElements: state.expandedList === "list2" ? null : 4, sidebar: props.sidebar || null, scope: "similar", - onFollow: { removeListProfileOnFollow }, }} /> From c9fa46e7889582f2b4a97df073c9257107251903 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Fri, 29 Sep 2023 16:13:29 +0200 Subject: [PATCH 05/33] feat() remove FollowButtonTracker to be handled as separate PR --- src/AccountProfile.jsx | 1 - .../Engagement/FollowButtonTracker.jsx | 168 ------------------ src/Recommender/Service/RecommendedUsers.jsx | 7 - 3 files changed, 176 deletions(-) delete mode 100644 src/Recommender/Engagement/FollowButtonTracker.jsx diff --git a/src/AccountProfile.jsx b/src/AccountProfile.jsx index 847debfc..14fcd1de 100644 --- a/src/AccountProfile.jsx +++ b/src/AccountProfile.jsx @@ -166,7 +166,6 @@ return ( becauseYouFollow: props.scope === "friends" ? props.becauseYouFollow : null, scope: props.scope, - onFollow: { removeListProfileOnFollow }, }} /> ); diff --git a/src/Recommender/Engagement/FollowButtonTracker.jsx b/src/Recommender/Engagement/FollowButtonTracker.jsx deleted file mode 100644 index 7d6c9279..00000000 --- a/src/Recommender/Engagement/FollowButtonTracker.jsx +++ /dev/null @@ -1,168 +0,0 @@ -///use: -// { { -// trackEngagement({ -// targetSigner: props.accountId, -// rank: props.rank, -// eventType: "people page avatar profile entry", -// }); -// }} -// href={profileUrl} -// > -// -// -// { -// trackEngagement({ -// targetSigner: props.accountId, -// rank: props.rank, -// eventType: "people page profile entry", -// }); -// }} -// > -// -// {props.profileName || profile.name || accountId.split(".near")[0]} -// -// -// @{accountId} -// -// - -// } - -/////////////// -const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test -// const dataplane = "https://near.dataplane.rudderstack.com"; //prod -const uri = "/v1/track"; -const api_url = `${dataplane}${uri}`; -const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test -// const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod -const currentTimeStamp = new Date().toISOString(); - -const trackEngagement = (targetSigner, eventType, rank) => { - const payload = { - userId: context.accountId, - event: eventType, - properties: { - targetSignerId: targetSigner, - rank: rank, - }, - timestamp: new Date().toISOString(), - }; - - asyncFetch(api_url, { - body: JSON.stringify({ payload }), - headers: { - "Content-Type": "application/json", - Authorization: auth, - }, - method: "POST", - }) - .then((response) => response.json()) - .then((data) => console.log(data)) - .catch((error) => console.log("Error:", error)); -}; -//////////// - -if ( - !props.accountId || - !context.accountId || - context.accountId === props.accountId -) { - return ""; -} - -const followEdge = Social.keys( - `${context.accountId}/graph/follow/${props.accountId}`, - undefined, - { - values_only: true, - } -); - -const inverseEdge = Social.keys( - `${props.accountId}/graph/follow/${context.accountId}`, - undefined, - { - values_only: true, - } -); - -const loading = followEdge === null || inverseEdge === null; -const isFollowing = Object.keys(followEdge || {}).length > 0; -const isInverse = Object.keys(inverseEdge || {}).length > 0; - -const type = follow ? "unfollow" : "follow"; - -const data = { - graph: { follow: { [props.accountId]: isFollowing ? null : "" } }, - index: { - graph: JSON.stringify({ - key: "follow", - value: { - type, - accountId: props.accountId, - }, - }), - notify: JSON.stringify({ - key: props.accountId, - value: { - type, - }, - }), - }, -}; - -const Wrapper = styled.div` - .follow-button { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 8px 16px; - height: 32px; - border-radius: 100px; - font-weight: 600; - font-size: 12px; - line-height: 15px; - text-align: center; - cursor: pointer; - background: #fbfcfd; - //background: red; - border: 1px solid #d7dbdf; - color: #006adc !important; - white-space: nowrap; - - &:hover, - &:focus { - background: #ecedee; - text-decoration: none; - outline: none; - } - - i { - color: #7e868c; - } - - .bi-16 { - font-size: 16px; - } - } -`; - -return ( - - - {isFollowing && } - {isFollowing ? "Following" : isInverse ? "Follow Back" : "Follow"} - - -); diff --git a/src/Recommender/Service/RecommendedUsers.jsx b/src/Recommender/Service/RecommendedUsers.jsx index fe0c0e6e..00af0a55 100644 --- a/src/Recommender/Service/RecommendedUsers.jsx +++ b/src/Recommender/Service/RecommendedUsers.jsx @@ -111,13 +111,7 @@ const loadMore = () => { } }; -const removeListProfileOnFollow = (rank) => { - const updatedUserData = userData.filter((rank) => rank !== rank); - State.update({ userData: [updatedUserData] }); -}; - if (state.isLoading) { - console.log("state:", state.isLoading); getRecommendedUsers(state.currentPage); } @@ -164,7 +158,6 @@ return ( profileName: user.profileName || null, sidebar: props.sidebar || null, scope: props.scope || null, - onFollow: { removeListProfileOnFollow }, }} /> From 2dd3960b38c73adcdd15e070499765844428fdca Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Fri, 29 Sep 2023 16:32:39 +0200 Subject: [PATCH 06/33] feat() resolve merge conflicts --- src/AccountProfile.jsx | 3 ++- src/AccountProfileOverlay.jsx | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/AccountProfile.jsx b/src/AccountProfile.jsx index 14fcd1de..e19486a4 100644 --- a/src/AccountProfile.jsx +++ b/src/AccountProfile.jsx @@ -119,7 +119,7 @@ const AccountProfile = ( Joined{" "} {" "} ago @@ -163,6 +163,7 @@ return ( profile, children: AccountProfile, placement: props.overlayPlacement, + verifications, becauseYouFollow: props.scope === "friends" ? props.becauseYouFollow : null, scope: props.scope, diff --git a/src/AccountProfileOverlay.jsx b/src/AccountProfileOverlay.jsx index 6f87a266..b915fc5c 100644 --- a/src/AccountProfileOverlay.jsx +++ b/src/AccountProfileOverlay.jsx @@ -58,6 +58,7 @@ const Card = styled.div` const FollowButtonWrapper = styled.div` width: 100%; + div, button { width: 100%; @@ -91,6 +92,38 @@ const overlay = ( )} + {verifications && (
+
+ + + + + + Verified User +
+ { verifications.human_provider && ( +
+ + + + + + + Human by {verifications.human_provider} +
+ )} + {verifications.kyc_provider && ( +
+ + + + + + KYC by {verifications.kyc_provider} +
+ )} +
)} + {props.scope === "friends" ? ( { State.update({ hasBeenFlagged: open }); }, - duration: 10000, + duration: 5000, }} /> )} From eca17328d59c714ccd302b5853df5c5665529bb0 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Sat, 30 Sep 2023 17:09:05 +0200 Subject: [PATCH 07/33] feat() Engagement tracker placed for LatestPeople and PeoplePage in Components Trending Users and Recommended Users with corresponding context --- src/AccountProfile.jsx | 124 ++++++++-------- src/AccountProfileOverlay.jsx | 135 ++++++++++++++---- src/LatestPeople.jsx | 6 +- src/PeoplePage.jsx | 10 +- .../Account/AccountProfileLargeCard.jsx | 60 +++----- .../Account/AccountProfileViewSwitch.jsx | 4 + .../CenteredLinksWrapperTracked.jsx | 56 ++++++++ .../Engagement/FollowButtonTracked.jsx | 28 ++++ src/Recommender/Engagement/ImageTracked.jsx | 31 ++++ .../Engagement/ProfileInfoTracked.jsx | 71 +++++++++ src/Recommender/Service/EngagementTracker.jsx | 39 +++++ src/Recommender/Service/RecommendedUsers.jsx | 6 +- .../Views/FriendsOfFriendsView.jsx | 2 + src/Recommender/Views/TrendingUsersView.jsx | 1 + 14 files changed, 436 insertions(+), 137 deletions(-) create mode 100644 src/Recommender/Engagement/CenteredLinksWrapperTracked.jsx create mode 100644 src/Recommender/Engagement/FollowButtonTracked.jsx create mode 100644 src/Recommender/Engagement/ImageTracked.jsx create mode 100644 src/Recommender/Engagement/ProfileInfoTracked.jsx create mode 100644 src/Recommender/Service/EngagementTracker.jsx diff --git a/src/AccountProfile.jsx b/src/AccountProfile.jsx index 2e12f116..3851b577 100644 --- a/src/AccountProfile.jsx +++ b/src/AccountProfile.jsx @@ -102,63 +102,63 @@ const AccountProfile = ( > - {verifications && ( - - - - + {verifications && ( + + + + + - )} + + )}
- - {profile.name || accountId.split(".near")[0]} - - - {props.inlineContent} - - {props.blockHeight && ( - - Joined{" "} - {" "} - ago - - )} - - {props.scope == "friends" && props.becauseYouFollow.length > 0 && ( - <> - - - - - {props.becauseYouFollow.length} friends follow - - - )} - - {props.scope == "similar" && ( - Shared interests - )} + + + {!props.hideAccountId && @{accountId}} @@ -169,17 +169,19 @@ const AccountProfile = ( if (props.noOverlay) return AccountProfile; return ( - + ); diff --git a/src/AccountProfileOverlay.jsx b/src/AccountProfileOverlay.jsx index 8be8116b..30307ef6 100644 --- a/src/AccountProfileOverlay.jsx +++ b/src/AccountProfileOverlay.jsx @@ -72,7 +72,7 @@ const VerificationText = styled.div` position: relative; top: 1px; font-size: 14px; - color: ${props => props.secondary ? '#717069' : 'black'}; + color: ${(props) => (props.secondary ? "#717069" : "black")}; `; const contentModerationItem = { @@ -103,37 +103,108 @@ const overlay = ( )}
- {verifications && (
-
- - - - - - Verified User -
- { verifications.human_provider && ( -
- - - - - + {verifications && ( +
+
+ + + + + + Verified User +
+ {verifications.human_provider && ( +
+ + + + + - Human by {verifications.human_provider} + + Human by{" "} + + {verifications.human_provider} + +
- )} - {verifications.kyc_provider && ( + )} + {verifications.kyc_provider && (
- - - - + + + + - KYC by {verifications.kyc_provider} + + KYC by{" "} + + {verifications.kyc_provider} + +
- )} -
)} + )} +
+ )} {props.scope === "friends" ? ( @@ -161,13 +232,15 @@ const overlay = ( props={{ tags, scroll: true }} /> )} - + {!!context.accountId && context.accountId !== props.accountId && ( @@ -207,7 +280,7 @@ return ( onOpenChange: () => { State.update({ hasBeenFlagged: false }); }, - duration: 5000 + duration: 5000, }} /> )} diff --git a/src/LatestPeople.jsx b/src/LatestPeople.jsx index 324decf1..3f9f15e3 100644 --- a/src/LatestPeople.jsx +++ b/src/LatestPeople.jsx @@ -37,6 +37,8 @@ const options = [ { text: "Recommended", value: "recommended" }, ]; +const fromContext = props; + const FlexContainer = styled.div` display: flex; align-items: center; @@ -135,7 +137,7 @@ return ( <> )} @@ -143,7 +145,7 @@ return ( <> )} diff --git a/src/PeoplePage.jsx b/src/PeoplePage.jsx index 64ef2f8d..d1908d31 100644 --- a/src/PeoplePage.jsx +++ b/src/PeoplePage.jsx @@ -3,6 +3,7 @@ let people = []; const peopleUrl = "#/${REPL_ACCOUNT}/widget/PeoplePage"; let followingData = null; let followersData = null; +const fromContext = props; State.init({ currentPage: 0, @@ -318,20 +319,23 @@ return ( )} {!context.accountId && state.selectedTab == "trending" && ( - + )} {context.accountId && state.selectedTab == "trending" && ( )} {context.accountId && state.selectedTab == "recommended" && ( )} diff --git a/src/Recommender/Account/AccountProfileLargeCard.jsx b/src/Recommender/Account/AccountProfileLargeCard.jsx index 03da4ad7..18bdf814 100644 --- a/src/Recommender/Account/AccountProfileLargeCard.jsx +++ b/src/Recommender/Account/AccountProfileLargeCard.jsx @@ -83,25 +83,7 @@ const TagsWrapper = styled.div` margin-top: -5px; `; -const TextLink = styled.a` - display: block; - margin: 0; - font-size: 14px; - line-height: 18px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; - outline: none; - max-width: 230px; - &:focus, - &:hover { - text-decoration: underline; - } -`; const ProfileListContainer = styled.div` width: auto; @@ -153,33 +135,31 @@ const LargeCard = styled.div` padding: 24px 0px 13px; `; -const CenteredLinksWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - padding-top: 14px; -`; return ( - - - {props.profileName || profile.name || accountId.split(".near")[0]} - - - @{accountId} - - + + {tags.length > 0 ? ( @@ -246,9 +226,11 @@ return ( {context.accountId && context.accountId !== props.accountId ? ( diff --git a/src/Recommender/Account/AccountProfileViewSwitch.jsx b/src/Recommender/Account/AccountProfileViewSwitch.jsx index b152c746..cc2f6a4a 100644 --- a/src/Recommender/Account/AccountProfileViewSwitch.jsx +++ b/src/Recommender/Account/AccountProfileViewSwitch.jsx @@ -10,6 +10,7 @@ return ( src="${REPL_ACCOUNT}/widget/AccountProfile" props={{ accountId: props.accountId, + accountIdRank: props.accountIdRank || null, sidebar: props.sidebar || null, followsYou: props.followsYou || null, becauseYouFollow: props.becauseYouFollow || null, @@ -20,6 +21,7 @@ return ( profileName: props.profileName || null, scope: props.scope || null, overlayPlacement: "left", + fromContext: props.fromContext, }} /> ) : ( @@ -27,6 +29,7 @@ return ( src="${REPL_ACCOUNT}/widget/Recommender.Account.AccountProfileLargeCard" props={{ accountId: props.accountId, + accountIdRank: props.accountIdRank || null, sidebar: props.sidebar || null, followsYou: props.followsYou || null, becauseYouFollow: props.becauseYouFollow || null, @@ -36,6 +39,7 @@ return ( profileImage: props.profileImage || null, profileName: props.profileName || null, scope: props.scope || null, + fromContext: props.fromContext, }} /> )} diff --git a/src/Recommender/Engagement/CenteredLinksWrapperTracked.jsx b/src/Recommender/Engagement/CenteredLinksWrapperTracked.jsx new file mode 100644 index 00000000..fa68b7d7 --- /dev/null +++ b/src/Recommender/Engagement/CenteredLinksWrapperTracked.jsx @@ -0,0 +1,56 @@ +const handleClick = () => { + State.update({ clicked: true }); +}; + +State.init({ + clicked: false, +}); + +const CenteredLinksWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +const TextLink = styled.a` + display: block; + margin: 0; + font-size: 14px; + line-height: 18px; + color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; + font-weight: ${(p) => (p.bold ? "600" : "400")}; + font-size: ${(p) => (p.small ? "12px" : "14px")}; + overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; + text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; + white-space: nowrap; + outline: none; + max-width: 230px; + + &:focus, + &:hover { + text-decoration: underline; + } +`; + +return ( +
+ + + + {props.profileName} + + + @{props.accountId} + + +
+); diff --git a/src/Recommender/Engagement/FollowButtonTracked.jsx b/src/Recommender/Engagement/FollowButtonTracked.jsx new file mode 100644 index 00000000..3b1369f9 --- /dev/null +++ b/src/Recommender/Engagement/FollowButtonTracked.jsx @@ -0,0 +1,28 @@ +const handleClick = () => { + State.update({ clicked: true }); +}; + +State.init({ + clicked: false, +}); + +return ( +
+ + +
+); diff --git a/src/Recommender/Engagement/ImageTracked.jsx b/src/Recommender/Engagement/ImageTracked.jsx new file mode 100644 index 00000000..86cca477 --- /dev/null +++ b/src/Recommender/Engagement/ImageTracked.jsx @@ -0,0 +1,31 @@ +const handleClick = () => { + State.update({ clicked: true }); +}; + +State.init({ + clicked: false, +}); + +return ( +
+ + +
+); \ No newline at end of file diff --git a/src/Recommender/Engagement/ProfileInfoTracked.jsx b/src/Recommender/Engagement/ProfileInfoTracked.jsx new file mode 100644 index 00000000..cac1cee0 --- /dev/null +++ b/src/Recommender/Engagement/ProfileInfoTracked.jsx @@ -0,0 +1,71 @@ +const handleClick = () => { + State.update({ clicked: true }); +}; + +State.init({ + clicked: false, +}); + +const Text = styled.p` + margin: 0; + font-size: 14px; + line-height: 20px; + color: ${(p) => (p.bold ? "#11181C" : "#687076")}; + font-weight: ${(p) => (p.bold ? "600" : "400")}; + font-size: ${(p) => (p.small ? "10px" : "14px")}; + overflow: ${(p) => (p.ellipsis ? "hidden" : "")}; + text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "")}; + white-space: nowrap; +`; + +return ( +
+ + + {props.profileName} + + + {props.inlineContent} + + {props.blockHeight && ( + + Joined{" "} + {" "} + ago + + )} + + {props.scope == "friends" && props.becauseYouFollow.length > 0 && ( + <> + + + + + {props.becauseYouFollow.length} friends follow + + + )} + + {props.scope == "similar" && ( + Shared interests + )} +
+); diff --git a/src/Recommender/Service/EngagementTracker.jsx b/src/Recommender/Service/EngagementTracker.jsx new file mode 100644 index 00000000..795037db --- /dev/null +++ b/src/Recommender/Service/EngagementTracker.jsx @@ -0,0 +1,39 @@ +const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test +// const dataplane = "https://near.dataplane.rudderstack.com"; //prod +const uri = "/v1/track"; +const api_url = `${dataplane}${uri}`; +const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test +// const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod +const currentTimeStamp = new Date().toISOString(); + +const trackEngagement = () => { + console.log("trackEngagement", props); + const payload = { + event: eventType, + properties: { + accountId: props.accountId, + accountIdRank: props.accountIdRank, + event: props.event, + component: props.context, + fromContext: props.fromContext, + }, + timestamp: new Date().toISOString(), + }; + + asyncFetch(api_url, { + body: JSON.stringify({ payload }), + headers: { + "Content-Type": "application/json", + Authorization: auth, + }, + method: "POST", + }) + .then((response) => response.json()) + .then((data) => console.log(data)) + .catch((error) => console.log(error)); +}; + +props.onClick ? trackEngagement(props) : null; +console.log(props); + +return <>{props.children}; diff --git a/src/Recommender/Service/RecommendedUsers.jsx b/src/Recommender/Service/RecommendedUsers.jsx index 00af0a55..79eb47b0 100644 --- a/src/Recommender/Service/RecommendedUsers.jsx +++ b/src/Recommender/Service/RecommendedUsers.jsx @@ -73,6 +73,9 @@ const updateState = (data, totalPageNum) => { }); }; +const passedContext = props.fromContext; +const fromContext = { ...passedContext, scope: props.scope || null }; + const getRecommendedUsers = (page) => { try { const url = `${props.dataset}_${page}.json`; @@ -141,11 +144,11 @@ return ( diff --git a/src/Recommender/Views/FriendsOfFriendsView.jsx b/src/Recommender/Views/FriendsOfFriendsView.jsx index be3874f9..540774a7 100644 --- a/src/Recommender/Views/FriendsOfFriendsView.jsx +++ b/src/Recommender/Views/FriendsOfFriendsView.jsx @@ -82,6 +82,7 @@ return ( returnElements: state.expandedList === "list1" ? null : 4, sidebar: props.sidebar || null, scope: "friends", + fromContext: props.fromContext, }} /> @@ -108,6 +109,7 @@ return ( returnElements: state.expandedList === "list2" ? null : 4, sidebar: props.sidebar || null, scope: "similar", + fromContext: props.fromContext, }} /> diff --git a/src/Recommender/Views/TrendingUsersView.jsx b/src/Recommender/Views/TrendingUsersView.jsx index 9cca5535..6aa49b78 100644 --- a/src/Recommender/Views/TrendingUsersView.jsx +++ b/src/Recommender/Views/TrendingUsersView.jsx @@ -17,6 +17,7 @@ return ( props={{ dataset: trendingProfilesURL, sidebar: props.sidebar, + fromContext: props.fromContext, }} /> From 29cac633a2850f61309cd84fdcf61f3e67ff997d Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Sat, 30 Sep 2023 17:16:36 +0200 Subject: [PATCH 08/33] feat() tracker switch over to prod dataplane --- src/Recommender/Service/EngagementTracker.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Recommender/Service/EngagementTracker.jsx b/src/Recommender/Service/EngagementTracker.jsx index 795037db..e87f688d 100644 --- a/src/Recommender/Service/EngagementTracker.jsx +++ b/src/Recommender/Service/EngagementTracker.jsx @@ -1,9 +1,9 @@ -const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test -// const dataplane = "https://near.dataplane.rudderstack.com"; //prod +// const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test +const dataplane = "https://near.dataplane.rudderstack.com"; //prod const uri = "/v1/track"; const api_url = `${dataplane}${uri}`; -const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test -// const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod +// const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test +const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod const currentTimeStamp = new Date().toISOString(); const trackEngagement = () => { From dcdea2b7faecd22c3379f4c910ab35a2e8040c2e Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Mon, 2 Oct 2023 17:10:57 +0200 Subject: [PATCH 09/33] fix() Fixing styling issues in Sidebar, outsourced Account components and added buttons linking PeoplePage --- src/AccountProfile.jsx | 111 ++----- src/AccountProfileOverlay.jsx | 190 +++--------- src/LatestPeople.jsx | 14 +- .../Account/AccountProfileOverlay.jsx | 293 ++++++++++++++++++ .../Account/AccountProfileSidebar.jsx | 183 +++++++++++ .../Account/AccountProfileViewSwitch.jsx | 3 +- .../Engagement/ProfileInfoTracked.jsx | 122 ++++++-- src/Recommender/Service/EngagementTracker.jsx | 2 - src/Recommender/Service/RecommendedUsers.jsx | 2 +- .../Views/FriendsOfFriendsView.jsx | 40 +++ src/Recommender/Views/TrendingUsersView.jsx | 61 +++- 11 files changed, 736 insertions(+), 285 deletions(-) create mode 100644 src/Recommender/Account/AccountProfileOverlay.jsx create mode 100644 src/Recommender/Account/AccountProfileSidebar.jsx diff --git a/src/AccountProfile.jsx b/src/AccountProfile.jsx index 3851b577..2ffe0238 100644 --- a/src/AccountProfile.jsx +++ b/src/AccountProfile.jsx @@ -22,39 +22,14 @@ const Wrapper = styled.a` min-width: 0; } - .hover-state1 { - opacity: 0; - } - - .hover-state2 { - opacity: 1; - } - &:hover, &:focus { - div.hover { + div:first-child { border-color: #d0d5dd; } - .hover-state1 { - opacity: 1; - transition: opacity 0.3s ease; - } - .hover-state2 { - opacity: 0; - transition: opacity 0.3s ease; - } } `; -const AvatarCount = styled.div` - color: rgb(104, 112, 118); - font-size: 14px; - font-weight: 300; - position: absolute; - right: 0px; - padding-right: 20px; -`; - const Text = styled.p` margin: 0; font-size: 14px; @@ -100,65 +75,44 @@ const AccountProfile = ( href={!props.onClick && profileUrl} onClick={props.onClick && (() => props.onClick(accountId))} > - + - {verifications && ( - - - - - + {verifications && ( + + + + - - )} + )}
- - - + + {profile.name || accountId.split(".near")[0]} + + + {props.inlineContent} + + {props.blockHeight && ( + + Joined{" "} + {" "} + ago + + )} {!props.hideAccountId && @{accountId}} @@ -173,15 +127,10 @@ return ( src="${REPL_ACCOUNT}/widget/AccountProfileOverlay" props={{ accountId: props.accountId, - accountIdRank: props.accountIdRank || null, profile, children: AccountProfile, placement: props.overlayPlacement, verifications, - becauseYouFollow: - props.scope === "friends" ? props.becauseYouFollow : null, - scope: props.scope || null, - fromContext: props.fromContext, }} /> -); +); \ No newline at end of file diff --git a/src/AccountProfileOverlay.jsx b/src/AccountProfileOverlay.jsx index 30307ef6..42b7814d 100644 --- a/src/AccountProfileOverlay.jsx +++ b/src/AccountProfileOverlay.jsx @@ -22,24 +22,6 @@ const CardWrapper = styled.div` padding: 6px; `; -const OverlayTagsWrapper = styled.div` - margin-left: 50px; -`; - -const AvatarCount = styled.div` - color: rgb(104, 112, 118); - font-size: 14px; - font-weight: 300; - padding-left: 12px; -`; - -const RecommendedAvatars = styled.div` - display: flex; - flex-direction: row; - align-items: center; - padding: 12px 0 0; -`; - const Card = styled.div` display: flex; flex-direction: column; @@ -52,10 +34,6 @@ const Card = styled.div` border: 1px solid #eceef0; box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); padding: 12px; - - div.layout { - margin-left: 63px; - } `; const FollowButtonWrapper = styled.div` @@ -72,7 +50,7 @@ const VerificationText = styled.div` position: relative; top: 1px; font-size: 14px; - color: ${(props) => (props.secondary ? "#717069" : "black")}; + color: ${props => props.secondary ? '#717069' : 'black'}; `; const contentModerationItem = { @@ -103,145 +81,47 @@ const overlay = ( )}
- {verifications && ( -
-
- - - - - - Verified User -
- {verifications.human_provider && ( -
- - - - - + {verifications && (
+
+ + + + + + Verified User +
+ { verifications.human_provider && ( +
+ + + + + - - Human by{" "} - - {verifications.human_provider} - - + Human by {verifications.human_provider}
- )} - {verifications.kyc_provider && ( + )} + {verifications.kyc_provider && (
- - - - + + + + - - KYC by{" "} - - {verifications.kyc_provider} - - + KYC by {verifications.kyc_provider}
- )} -
- )} - - {props.scope === "friends" ? ( - - - - - - {props.becauseYouFollow.length} friends following - - - - ) : ( - - )} + )} +
)} + {!!context.accountId && context.accountId !== props.accountId && ( )} @@ -280,10 +160,10 @@ return ( onOpenChange: () => { State.update({ hasBeenFlagged: false }); }, - duration: 5000, + duration: 5000 }} /> )}
-); +); \ No newline at end of file diff --git a/src/LatestPeople.jsx b/src/LatestPeople.jsx index 3f9f15e3..a6ea5f8e 100644 --- a/src/LatestPeople.jsx +++ b/src/LatestPeople.jsx @@ -137,7 +137,12 @@ return ( <> )} @@ -145,7 +150,12 @@ return ( <> )} diff --git a/src/Recommender/Account/AccountProfileOverlay.jsx b/src/Recommender/Account/AccountProfileOverlay.jsx new file mode 100644 index 00000000..64f55c03 --- /dev/null +++ b/src/Recommender/Account/AccountProfileOverlay.jsx @@ -0,0 +1,293 @@ +const accountId = props.accountId; +const profile = props.profile || Social.get(`${accountId}/profile/**`, "final"); +const tags = Object.keys(profile.tags || {}); +const profileUrl = `#/${REPL_ACCOUNT}/widget/ProfilePage?accountId=${accountId}`; +const verifications = props.verifications; +const overlayStyles = props.overlayStyles; + +const handleOnMouseEnter = () => { + State.update({ show: true }); +}; +const handleOnMouseLeave = () => { + State.update({ show: false }); +}; + +State.init({ + show: false, + hasBeenFlagged: false, +}); + +const CardWrapper = styled.div` + z-index: 100; + padding: 6px; +`; + +const OverlayTagsWrapper = styled.div` + margin-left: 50px; +`; + +const AvatarCount = styled.div` + color: rgb(104, 112, 118); + font-size: 14px; + font-weight: 300; + padding-left: 12px; +`; + +const RecommendedAvatars = styled.div` + display: flex; + flex-direction: row; + align-items: center; + padding: 12px 0 0; +`; + +const Card = styled.div` + display: flex; + flex-direction: column; + justify-content: stretch; + gap: 12px; + width: 275px; + border-radius: 12px; + z-index: 1070; + background: #fff; + border: 1px solid #eceef0; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); + padding: 12px; + + div.layout { + margin-left: 63px; + } +`; + +const FollowButtonWrapper = styled.div` + width: 100px; + + div, + button { + width: 100%; + } +`; + +const VerificationText = styled.div` + display: inline; + position: relative; + top: 1px; + font-size: 14px; + color: ${(props) => (props.secondary ? "#717069" : "black")}; +`; + +const contentModerationItem = { + type: "social", + path: profileUrl, + reportedBy: context.accountId, +}; + +const overlay = ( + + +
+ + {!!context.accountId && context.accountId !== props.accountId && ( + { + State.update({ hasBeenFlagged: true }); + }, + }} + /> + )} +
+ + {verifications && ( +
+
+ + + + + + Verified User +
+ {verifications.human_provider && ( +
+ + + + + + + + Human by{" "} + + {verifications.human_provider} + + +
+ )} + {verifications.kyc_provider && ( +
+ + + + + + + KYC by{" "} + + {verifications.kyc_provider} + + +
+ )} +
+ )} + + {props.scope === "friends" ? ( + + + + + + {props.becauseYouFollow.length} friends following + + + + ) : ( + + )} + + {!!context.accountId && context.accountId !== props.accountId && ( + + + + )} +
+
+); + +return ( + +
+ {props.children || "Hover Me"} + + {state.hasBeenFlagged && ( + { + State.update({ hasBeenFlagged: false }); + }, + duration: 5000, + }} + /> + )} +
+
+); diff --git a/src/Recommender/Account/AccountProfileSidebar.jsx b/src/Recommender/Account/AccountProfileSidebar.jsx new file mode 100644 index 00000000..91e08b95 --- /dev/null +++ b/src/Recommender/Account/AccountProfileSidebar.jsx @@ -0,0 +1,183 @@ +const accountId = props.accountId || context.accountId; +const profile = props.profile || Social.get(`${accountId}/profile/**`, "final"); +const profileUrl = `#/${REPL_ACCOUNT}/widget/ProfilePage?accountId=${accountId}`; +const verifications = props.verifications; + +const Wrapper = styled.a` + display: inline-grid; + width: 100%; + align-items: center; + gap: 12px; + grid-template-columns: auto 1fr; + cursor: pointer; + margin: 0; + color: #687076 !important; + outline: none; + text-decoration: none !important; + background: none !important; + border: none; + text-align: left; + padding: 0; + + > * { + min-width: 0; + } + + .hover-state1 { + opacity: 0; + } + + .hover-state2 { + opacity: 1; + } + + &:hover, + &:focus { + div.hover { + border-color: #d0d5dd; + } + .hover-state1 { + opacity: 1; + transition: opacity 0.3s ease; + } + .hover-state2 { + opacity: 0; + transition: opacity 0.3s ease; + } + } +`; + +const AvatarCount = styled.div` + color: rgb(104, 112, 118); + font-size: 14px; + font-weight: 300; + position: absolute; + right: 0px; + padding-right: 20px; +`; + +const Text = styled.p` + margin: 0; + font-size: 14px; + line-height: 20px; + color: ${(p) => (p.bold ? "#11181C" : "#687076")}; + font-weight: ${(p) => (p.bold ? "600" : "400")}; + font-size: ${(p) => (p.small ? "10px" : "14px")}; + overflow: ${(p) => (p.ellipsis ? "hidden" : "")}; + text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "")}; + white-space: nowrap; +`; + +const Avatar = styled.div` + width: ${props.avatarSize || "40px"}; + height: ${props.avatarSize || "40px"}; + flex-shrink: 0; + border: 1px solid #eceef0; + overflow: hidden; + border-radius: 40px; + transition: border-color 200ms; + + img { + object-fit: cover; + width: 100%; + height: 100%; + } +`; + +const VerifiedBadge = styled.div` + position: absolute; + left: 24px; + top: 22px; +`; +const Name = styled.div` + display: flex; + gap: 6px; + align-items: center; +`; + +const AccountProfile = ( + props.onClick(accountId))} + > + + + + + {verifications && ( + + + + + + + + )} + +
+ + + +
+
+); + +if (props.noOverlay) return AccountProfile; + +return ( + +); diff --git a/src/Recommender/Account/AccountProfileViewSwitch.jsx b/src/Recommender/Account/AccountProfileViewSwitch.jsx index cc2f6a4a..47928f50 100644 --- a/src/Recommender/Account/AccountProfileViewSwitch.jsx +++ b/src/Recommender/Account/AccountProfileViewSwitch.jsx @@ -7,7 +7,7 @@ return ( <> {props.sidebar ? ( ) : ( diff --git a/src/Recommender/Engagement/ProfileInfoTracked.jsx b/src/Recommender/Engagement/ProfileInfoTracked.jsx index cac1cee0..e32dfa45 100644 --- a/src/Recommender/Engagement/ProfileInfoTracked.jsx +++ b/src/Recommender/Engagement/ProfileInfoTracked.jsx @@ -6,6 +6,45 @@ State.init({ clicked: false, }); +const Wrapper = styled.a` + text-decoration: none !important; + + .solid-state { + position: absolute; + margin-right: 26px; + right: 0; + } + + .hover-state1 { + position: absolute; + right: 0; + margin-right: 26px; + opacity: 0; + } + + .hover-state2 { + position: absolute; + right: 0; + margin-right: 26px; + opacity: 1; + } + + &:hover, + &:focus { + div.hover { + border-color: #d0d5dd; + } + .hover-state1 { + opacity: 1; + transition: opacity 0.3s ease; + } + .hover-state2 { + opacity: 0; + transition: opacity 0.3s ease; + } + } +`; + const Text = styled.p` margin: 0; font-size: 14px; @@ -16,10 +55,25 @@ const Text = styled.p` overflow: ${(p) => (p.ellipsis ? "hidden" : "")}; text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "")}; white-space: nowrap; + width: ${props.scope ? "100px" : "170px"}; +`; + +const AvatarCount = styled.div` + color: rgb(104, 112, 118); + font-size: 14px; + font-weight: 300; + padding-left: 12px; `; return ( -
+
+ + {props.scope == "friends" && props.becauseYouFollow.length > 0 && ( + + + + + + {props.becauseYouFollow.length} friends follow + + + )} + {props.scope == "similar" && ( + + Shared interests + + )} + {props.profileName} + @{props.accountId} {props.inlineContent} - - {props.blockHeight && ( - - Joined{" "} - {" "} - ago - - )} - - {props.scope == "friends" && props.becauseYouFollow.length > 0 && ( - <> - - - - - {props.becauseYouFollow.length} friends follow - - - )} - - {props.scope == "similar" && ( - Shared interests - )} + {props.blockHeight && ( + + Joined{" "} + {" "} + ago + + )}
); diff --git a/src/Recommender/Service/EngagementTracker.jsx b/src/Recommender/Service/EngagementTracker.jsx index e87f688d..87960d67 100644 --- a/src/Recommender/Service/EngagementTracker.jsx +++ b/src/Recommender/Service/EngagementTracker.jsx @@ -7,7 +7,6 @@ const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod const currentTimeStamp = new Date().toISOString(); const trackEngagement = () => { - console.log("trackEngagement", props); const payload = { event: eventType, properties: { @@ -34,6 +33,5 @@ const trackEngagement = () => { }; props.onClick ? trackEngagement(props) : null; -console.log(props); return <>{props.children}; diff --git a/src/Recommender/Service/RecommendedUsers.jsx b/src/Recommender/Service/RecommendedUsers.jsx index 79eb47b0..d96d2376 100644 --- a/src/Recommender/Service/RecommendedUsers.jsx +++ b/src/Recommender/Service/RecommendedUsers.jsx @@ -38,7 +38,7 @@ const Profile = styled.div``; const Profiles = styled.div` display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-template-columns: ${props.gridCols ? props.gridCols : "repeat(4, minmax(0, 1fr))"}; gap: 24px; @media (max-width: 1024px) { diff --git a/src/Recommender/Views/FriendsOfFriendsView.jsx b/src/Recommender/Views/FriendsOfFriendsView.jsx index 540774a7..801546b1 100644 --- a/src/Recommender/Views/FriendsOfFriendsView.jsx +++ b/src/Recommender/Views/FriendsOfFriendsView.jsx @@ -1,3 +1,31 @@ +const ButtonLink = styled.a` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + const CategoryHeader = styled.div` display: flex; justify-content: space-between; @@ -83,6 +111,7 @@ return ( sidebar: props.sidebar || null, scope: "friends", fromContext: props.fromContext, + gridCols: props.gridCols, }} /> @@ -110,9 +139,20 @@ return ( sidebar: props.sidebar || null, scope: "similar", fromContext: props.fromContext, + gridCols: props.gridCols, }} /> )} + + {props.sidebar ? ( + <> + + View Recommended Profiles + + + ) : ( + <> + )} ); diff --git a/src/Recommender/Views/TrendingUsersView.jsx b/src/Recommender/Views/TrendingUsersView.jsx index 6aa49b78..f6be41f1 100644 --- a/src/Recommender/Views/TrendingUsersView.jsx +++ b/src/Recommender/Views/TrendingUsersView.jsx @@ -1,3 +1,31 @@ +const ButtonLink = styled.a` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + const TrendingUsersView = styled.div` display: flex; flex-direction: column; @@ -11,14 +39,27 @@ const algorithm = "trending_users"; const trendingProfilesURL = `${BASE_URL}/${algorithm}_page`; return ( - - - + <> + + + + {props.sidebar ? ( + <> + + View Trending Users + + + ) : ( + <> + )} + ); From c25e78c0a97bd7f675a7fca21469443f232cfe95 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Mon, 2 Oct 2023 22:10:27 +0200 Subject: [PATCH 10/33] Apply suggestions from code review Co-authored-by: Dmitriy <34593263+shelegdmitriy@users.noreply.github.com> --- src/LatestPeople.jsx | 2 +- src/PeoplePage.jsx | 2 +- src/Recommender/Account/AccountProfileLargeCard.jsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LatestPeople.jsx b/src/LatestPeople.jsx index a6ea5f8e..9924df64 100644 --- a/src/LatestPeople.jsx +++ b/src/LatestPeople.jsx @@ -119,7 +119,7 @@ return ( {accounts.map((account) => ( ( { let newValue = value; @@ -162,7 +162,7 @@ return ( {tags.length > 0 ? ( - + ) : ( From 375b6e862d4844f7a11290170f4099c58770b7e3 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Tue, 3 Oct 2023 15:50:53 +0200 Subject: [PATCH 11/33] Address PR request, removing condition for tags component on null --- .../Account/AccountProfileLargeCard.jsx | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Recommender/Account/AccountProfileLargeCard.jsx b/src/Recommender/Account/AccountProfileLargeCard.jsx index 060084a3..99dc2431 100644 --- a/src/Recommender/Account/AccountProfileLargeCard.jsx +++ b/src/Recommender/Account/AccountProfileLargeCard.jsx @@ -51,7 +51,7 @@ const CurrentUserProfile = styled.div` `; const NoTags = styled.div` - height: 20px; + height: 13px; `; const Score = styled.li` @@ -83,8 +83,6 @@ const TagsWrapper = styled.div` margin-top: -5px; `; - - const ProfileListContainer = styled.div` width: auto; position: relative; @@ -150,21 +148,25 @@ return ( /> + + + + - {tags.length > 0 ? ( - - - - ) : ( + {tags.length == 0 && ( From a02741c024e4328e435e20f2a83a3e71e79dc275 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Wed, 4 Oct 2023 12:43:55 +0200 Subject: [PATCH 12/33] feat() Adjust tracker payload to align with rudderstack required Common Fields --- src/Recommender/Service/EngagementTracker.jsx | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Recommender/Service/EngagementTracker.jsx b/src/Recommender/Service/EngagementTracker.jsx index 87960d67..4868a9bd 100644 --- a/src/Recommender/Service/EngagementTracker.jsx +++ b/src/Recommender/Service/EngagementTracker.jsx @@ -4,19 +4,32 @@ const uri = "/v1/track"; const api_url = `${dataplane}${uri}`; // const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod -const currentTimeStamp = new Date().toISOString(); + +const generateAnonId = (length) => { + const characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + + for (let i = 0; i < length; i++) { + const randomIndex = Math.floor(Math.random() * characters.length); + result += characters.charAt(randomIndex); + } + return result; +}; const trackEngagement = () => { const payload = { - event: eventType, + anonymousId: generateAnonId(24), + userId: "", + channel: "sources", + context: props.fromContext, + type: "track", + originalTimestamp: new Date().toISOString(), + sentAt: new Date().toISOString(), + event: props.event, properties: { - accountId: props.accountId, accountIdRank: props.accountIdRank, - event: props.event, - component: props.context, - fromContext: props.fromContext, }, - timestamp: new Date().toISOString(), }; asyncFetch(api_url, { From c33ae8a158bd4583299fa68eeba99d4c43fcd60d Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Wed, 4 Oct 2023 16:56:37 +0200 Subject: [PATCH 13/33] fix() Mitigating the scroll-y position bug that reset the viewport on loadmore by repeating the service function for each view independently --- src/AccountProfileOverlay.jsx | 200 ++++++++++++++---- src/LatestPeople.jsx | 4 +- src/PeoplePage.jsx | 8 +- .../Account/AccountProfileSidebar.jsx | 2 +- src/Recommender/Service/EngagementTracker.jsx | 9 +- src/Recommender/Service/RecommendedUsers.jsx | 21 +- src/Recommender/Views/FriendsOfFriends.jsx | 174 +++++++++++++++ ...dsView.jsx => RecommendedUsersSidebar.jsx} | 0 .../Views/RecommendedUsersView.jsx | 154 ++++++++++++++ src/Recommender/Views/SimilarProfiles.jsx | 174 +++++++++++++++ .../Views/TrendingUsersSidebar.jsx | 63 ++++++ src/Recommender/Views/TrendingUsersView.jsx | 190 ++++++++++++++--- 12 files changed, 917 insertions(+), 82 deletions(-) create mode 100644 src/Recommender/Views/FriendsOfFriends.jsx rename src/Recommender/Views/{FriendsOfFriendsView.jsx => RecommendedUsersSidebar.jsx} (100%) create mode 100644 src/Recommender/Views/RecommendedUsersView.jsx create mode 100644 src/Recommender/Views/SimilarProfiles.jsx create mode 100644 src/Recommender/Views/TrendingUsersSidebar.jsx diff --git a/src/AccountProfileOverlay.jsx b/src/AccountProfileOverlay.jsx index 42b7814d..604c4ef1 100644 --- a/src/AccountProfileOverlay.jsx +++ b/src/AccountProfileOverlay.jsx @@ -50,7 +50,7 @@ const VerificationText = styled.div` position: relative; top: 1px; font-size: 14px; - color: ${props => props.secondary ? '#717069' : 'black'}; + color: ${(props) => (props.secondary ? "#717069" : "black")}; `; const contentModerationItem = { @@ -63,10 +63,22 @@ const overlay = (
- + {props.sidebar && ( + + )} + {!props.sidebar && ( + + )} {!!context.accountId && context.accountId !== props.accountId && ( - {verifications && (
-
- - - - - - Verified User -
- { verifications.human_provider && ( -
- - - - - + {verifications && ( +
+
+ + + + + + Verified User +
+ {verifications.human_provider && ( +
+ + + + + - Human by {verifications.human_provider} + + Human by{" "} + + {verifications.human_provider} + +
- )} - {verifications.kyc_provider && ( + )} + {verifications.kyc_provider && (
- - - - + + + + - KYC by {verifications.kyc_provider} + + KYC by{" "} + + {verifications.kyc_provider} + +
- )} -
)} + )} +
+ )} - - {!!context.accountId && context.accountId !== props.accountId && ( - + {props.scope === "friends" ? ( + + + + + {props.becauseYouFollow.length} friends following + + + + ) : ( + + )} + + {!!context.accountId && context.accountId !== props.accountId && ( + + {props.sidebar && ( + + )} + {!props.sidebar && ( + + )} )} @@ -160,10 +278,10 @@ return ( onOpenChange: () => { State.update({ hasBeenFlagged: false }); }, - duration: 5000 + duration: 5000, }} /> )}
-); \ No newline at end of file +); diff --git a/src/LatestPeople.jsx b/src/LatestPeople.jsx index 9924df64..05e14435 100644 --- a/src/LatestPeople.jsx +++ b/src/LatestPeople.jsx @@ -136,7 +136,7 @@ return ( {state.selectedView.value === "trending" && ( <> )} diff --git a/src/Recommender/Account/AccountProfileSidebar.jsx b/src/Recommender/Account/AccountProfileSidebar.jsx index 91e08b95..2f578cd7 100644 --- a/src/Recommender/Account/AccountProfileSidebar.jsx +++ b/src/Recommender/Account/AccountProfileSidebar.jsx @@ -172,7 +172,7 @@ return ( accountIdRank: props.accountIdRank || null, profile, children: AccountProfile, - placement: "left", + placement: props.overlayPlacement, verifications, becauseYouFollow: props.scope === "friends" ? props.becauseYouFollow : null, diff --git a/src/Recommender/Service/EngagementTracker.jsx b/src/Recommender/Service/EngagementTracker.jsx index 4868a9bd..9a0056c4 100644 --- a/src/Recommender/Service/EngagementTracker.jsx +++ b/src/Recommender/Service/EngagementTracker.jsx @@ -1,9 +1,10 @@ -// const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test -const dataplane = "https://near.dataplane.rudderstack.com"; //prod +const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test +// const dataplane = "https://near.dataplane.rudderstack.com"; //prod const uri = "/v1/track"; const api_url = `${dataplane}${uri}`; // const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test -const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod +const auth = "Basic MldJT1o0aFAwV3FKUnJKbUNsejFDek1RVVBqOg=="; //test2 +// const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod const generateAnonId = (length) => { const characters = @@ -21,7 +22,7 @@ const trackEngagement = () => { const payload = { anonymousId: generateAnonId(24), userId: "", - channel: "sources", + channel: "web", context: props.fromContext, type: "track", originalTimestamp: new Date().toISOString(), diff --git a/src/Recommender/Service/RecommendedUsers.jsx b/src/Recommender/Service/RecommendedUsers.jsx index d96d2376..e82a596a 100644 --- a/src/Recommender/Service/RecommendedUsers.jsx +++ b/src/Recommender/Service/RecommendedUsers.jsx @@ -38,7 +38,9 @@ const Profile = styled.div``; const Profiles = styled.div` display: grid; - grid-template-columns: ${props.gridCols ? props.gridCols : "repeat(4, minmax(0, 1fr))"}; + grid-template-columns: ${props.gridCols + ? props.gridCols + : "repeat(4, minmax(0, 1fr))"}; gap: 24px; @media (max-width: 1024px) { @@ -73,9 +75,18 @@ const updateState = (data, totalPageNum) => { }); }; +const displayedUsers = props.returnElements + ? state.userData.slice(0, props.returnElements) + : state.userData; + const passedContext = props.fromContext; const fromContext = { ...passedContext, scope: props.scope || null }; +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; + + const getRecommendedUsers = (page) => { try { const url = `${props.dataset}_${page}.json`; @@ -87,6 +98,7 @@ const getRecommendedUsers = (page) => { updateState(parsedResults.data, totalPageNum); } else { console.log( + res, "Error fetching data. Try reloading the page, or no data available." ); } @@ -97,7 +109,7 @@ const getRecommendedUsers = (page) => { const totalPageNum = parsedResults.total_pages || 10; updateState(parsedResults.data, totalPageNum); } else { - console.log("Error fetching data. Try reloading the page."); + console.log(res, "Error fetching data. Try reloading the page."); } }); } @@ -118,11 +130,6 @@ if (state.isLoading) { getRecommendedUsers(state.currentPage); } -const returnElements = props.returnElements; -const displayedUsers = returnElements - ? state.userData.slice(0, returnElements) - : state.userData; - return ( {state.isLoading && displayedUsers.length == null &&

Loading...

} diff --git a/src/Recommender/Views/FriendsOfFriends.jsx b/src/Recommender/Views/FriendsOfFriends.jsx new file mode 100644 index 00000000..098f8c0f --- /dev/null +++ b/src/Recommender/Views/FriendsOfFriends.jsx @@ -0,0 +1,174 @@ +const Button = styled.button` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + +const Profile = styled.div``; + +const Profiles = styled.div` + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 24px; + + @media (max-width: 1024px) { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + @media (max-width: 800px) { + grid-template-columns: minmax(0, 1fr); + } +`; + +const RecommendedUsers = styled.div` + display: flex; + flex-direction: column; + gap: 12px; + padding-bottom: 100px; +`; + +State.init({ + currentPage: 1, + userData: [], + isLoading: true, + error: null, + totalPages: 1, +}); + +const updateState = (data, totalPageNum) => { + State.update({ + isLoading: false, + userData: [...state.userData, ...data], + totalPages: totalPageNum, + }); +}; + +const displayedUsers = props.returnElements + ? state.userData.slice(0, props.returnElements) + : state.userData; + +const passedContext = props.fromContext; +const fromContext = { ...passedContext, scope: props.scope || null }; + +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; +const algorithm = "friends_of_friends"; +const dataset = `${BASE_URL}/${algorithm}_${props.accountId}`; + +const getRecommendedUsers = (page) => { + try { + const url = `${dataset}_${page}.json`; + if (state.currentPage == 1) { + const res = fetch(url); + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log( + res, + "Error fetching data. Try reloading the page, or no data available." + ); + } + } else { + asyncFetch(url).then((res) => { + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log(res, "Error fetching data. Try reloading the page."); + } + }); + } + } catch (error) { + console.log(error.message); + } +}; + +const loadMore = () => { + const nextPage = state.currentPage + 1; + if (nextPage <= state.totalPages) { + State.update({ currentPage: nextPage }); + getRecommendedUsers(nextPage); + } +}; + +if (state.isLoading) { + getRecommendedUsers(state.currentPage); +} + +return ( + + {state.isLoading && displayedUsers.length == null &&

Loading...

} + {state.error &&

Error: {state.error}

} + {(displayedUsers.length < 4 || displayedUsers == null) && + state.isLoading && + (props.scope == "friends" || props.scope === "similar") && ( +

+ Follow More Users to Unlock More Personalized Recommendations, See + Who’s + + Trending + +

+ )} + + {displayedUsers.map((user, index) => ( + + + + ))} + + {!props.returnElements && state.currentPage < state.totalPages ? ( + + ) : null} +
+); diff --git a/src/Recommender/Views/FriendsOfFriendsView.jsx b/src/Recommender/Views/RecommendedUsersSidebar.jsx similarity index 100% rename from src/Recommender/Views/FriendsOfFriendsView.jsx rename to src/Recommender/Views/RecommendedUsersSidebar.jsx diff --git a/src/Recommender/Views/RecommendedUsersView.jsx b/src/Recommender/Views/RecommendedUsersView.jsx new file mode 100644 index 00000000..665a8b55 --- /dev/null +++ b/src/Recommender/Views/RecommendedUsersView.jsx @@ -0,0 +1,154 @@ +const ButtonLink = styled.a` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + +const CategoryHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const RecommendationsView = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + +const H3 = styled.h3` + font-weight: 600; + font-size: 24px; + line-height: normal; + color: #11181c; + margin: 1.8rem 0 1.5rem 0; +`; + +const RetroLinkButton = styled.button` + background: none; + border: none; + cursor: pointer; + color: blue; + font-size: 16px; + padding: 0; + margin: 0; + outline: none; + transition: color 0.3s; + + &:hover { + color: purple; + } + + &:active { + color: red; + } +`; + +State.init({ + expandedList: "", +}); + +const handleToggleList = (listId) => { + if (state.expandedList === listId) { + State.update({ expandedList: "" }); + } else { + State.update({ expandedList: listId }); + } +}; + +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; + +const similarProfiles = "similarity_estimation"; + +const recommendedProfilesURL = `${BASE_URL}/${recommendedProfiles}_${context.accountId}`; +const similarProfilesURL = `${BASE_URL}/${similarProfiles}_${context.accountId}`; + +return ( + + {state.expandedList !== "list2" && ( + <> + {props.sidebar ? ( + <> + ) : ( + +

Friends of Friends

+ handleToggleList("list1")}> + {state.expandedList === "list1" + ? "Back to categories" + : "View all"} + +
+ )} + + + )} + + {state.expandedList !== "list1" && ( + <> + {props.sidebar ? ( + <> + ) : ( + +

Similar to you

+ handleToggleList("list2")}> + {state.expandedList === "list2" + ? "Back to categories" + : "View all"} + +
+ )} + + + )} + + {props.sidebar && ( + + View Recommended Profiles + + )} +
+); diff --git a/src/Recommender/Views/SimilarProfiles.jsx b/src/Recommender/Views/SimilarProfiles.jsx new file mode 100644 index 00000000..d0b94484 --- /dev/null +++ b/src/Recommender/Views/SimilarProfiles.jsx @@ -0,0 +1,174 @@ +const Button = styled.button` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + +const Profile = styled.div``; + +const Profiles = styled.div` + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 24px; + + @media (max-width: 1024px) { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + @media (max-width: 800px) { + grid-template-columns: minmax(0, 1fr); + } +`; + +const RecommendedUsers = styled.div` + display: flex; + flex-direction: column; + gap: 12px; + padding-bottom: 100px; +`; + +State.init({ + currentPage: 1, + userData: [], + isLoading: true, + error: null, + totalPages: 1, +}); + +const updateState = (data, totalPageNum) => { + State.update({ + isLoading: false, + userData: [...state.userData, ...data], + totalPages: totalPageNum, + }); +}; + +const displayedUsers = props.returnElements + ? state.userData.slice(0, props.returnElements) + : state.userData; + +const passedContext = props.fromContext; +const fromContext = { ...passedContext, scope: props.scope || null }; + +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; +const algorithm = "similarity_estimation"; +const dataset = `${BASE_URL}/${algorithm}_${props.accountId}`; + +const getRecommendedUsers = (page) => { + try { + const url = `${dataset}_${page}.json`; + if (state.currentPage == 1) { + const res = fetch(url); + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log( + res, + "Error fetching data. Try reloading the page, or no data available." + ); + } + } else { + asyncFetch(url).then((res) => { + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log(res, "Error fetching data. Try reloading the page."); + } + }); + } + } catch (error) { + console.log(error.message); + } +}; + +const loadMore = () => { + const nextPage = state.currentPage + 1; + if (nextPage <= state.totalPages) { + State.update({ currentPage: nextPage }); + getRecommendedUsers(nextPage); + } +}; + +if (state.isLoading) { + getRecommendedUsers(state.currentPage); +} + +return ( + + {state.isLoading && displayedUsers.length == null &&

Loading...

} + {state.error &&

Error: {state.error}

} + {(displayedUsers.length < 4 || displayedUsers == null) && + state.isLoading && + (props.scope == "friends" || props.scope === "similar") && ( +

+ Follow More Users to Unlock More Personalized Recommendations, See + Who’s + + Trending + +

+ )} + + {displayedUsers.map((user, index) => ( + + + + ))} + + {!props.returnElements && state.currentPage < state.totalPages ? ( + + ) : null} +
+); diff --git a/src/Recommender/Views/TrendingUsersSidebar.jsx b/src/Recommender/Views/TrendingUsersSidebar.jsx new file mode 100644 index 00000000..55aad43b --- /dev/null +++ b/src/Recommender/Views/TrendingUsersSidebar.jsx @@ -0,0 +1,63 @@ +const ButtonLink = styled.a` + display: block; + width: 100%; + padding: 8px; + height: 32px; + background: #fbfcfd; + border: 1px solid #d7dbdf; + border-radius: 50px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + color: #11181c !important; + margin: 0; + + &:hover, + &:focus { + background: #ecedee; + text-decoration: none; + outline: none; + } + + span { + color: #687076 !important; + } +`; + +const TrendingUsersView = styled.div` + display: flex; + flex-direction: column; + gap: 12px; +`; + +const STORE = "storage.googleapis.com"; +const BUCKET = "databricks-near-query-runner"; +const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; +const algorithm = "trending_users"; +const dataset = `${BASE_URL}/${algorithm}`; + +return ( + <> + + + + {props.sidebar && ( + <> + + View Trending Users + + + )} + +); diff --git a/src/Recommender/Views/TrendingUsersView.jsx b/src/Recommender/Views/TrendingUsersView.jsx index f6be41f1..fb1f90ef 100644 --- a/src/Recommender/Views/TrendingUsersView.jsx +++ b/src/Recommender/Views/TrendingUsersView.jsx @@ -1,4 +1,4 @@ -const ButtonLink = styled.a` +const Button = styled.button` display: block; width: 100%; padding: 8px; @@ -26,40 +26,180 @@ const ButtonLink = styled.a` } `; -const TrendingUsersView = styled.div` +const Profile = styled.div``; + +const Profiles = styled.div` + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 24px; + + @media (max-width: 1024px) { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + @media (max-width: 800px) { + grid-template-columns: minmax(0, 1fr); + } +`; + +const RecommendedUsers = styled.div` display: flex; flex-direction: column; gap: 12px; + padding-bottom: 100px; `; +State.init({ + currentPage: 1, + userData: [], + isLoading: true, + error: null, + totalPages: 1, +}); + +const updateState = (data, totalPageNum) => { + State.update({ + isLoading: false, + userData: [...state.userData, ...data], + totalPages: totalPageNum, + }); +}; + +const displayedUsers = props.returnElements + ? state.userData.slice(0, props.returnElements) + : state.userData; + +const passedContext = props.fromContext; +const fromContext = { ...passedContext, scope: props.scope || null }; + const STORE = "storage.googleapis.com"; const BUCKET = "databricks-near-query-runner"; const BASE_URL = `https://${STORE}/${BUCKET}/output/recommendations`; const algorithm = "trending_users"; -const trendingProfilesURL = `${BASE_URL}/${algorithm}_page`; +const dataset = `${BASE_URL}/${algorithm}`; + +const getRecommendedUsers = (page) => { + try { + const url = `${dataset}_${page}.json`; + if (state.currentPage == 1) { + const res = fetch(url); + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log( + res, + "Error fetching data. Try reloading the page, or no data available." + ); + } + } else { + asyncFetch(url).then((res) => { + if (res.ok) { + const parsedResults = JSON.parse(res.body); + const totalPageNum = parsedResults.total_pages || 10; + updateState(parsedResults.data, totalPageNum); + } else { + console.log(res, "Error fetching data. Try reloading the page."); + } + }); + } + } catch (error) { + console.log(error.message); + } +}; + +const loadMore = () => { + const nextPage = state.currentPage + 1; + if (nextPage <= state.totalPages) { + State.update({ currentPage: nextPage }); + getRecommendedUsers(nextPage); + } +}; + +if (state.isLoading) { + getRecommendedUsers(state.currentPage); +} return ( - <> - - - - {props.sidebar ? ( - <> - - View Trending Users - - - ) : ( - <> + + {state.isLoading && displayedUsers.length == null &&

Loading...

} + {state.error &&

Error: {state.error}

} + {(displayedUsers.length < 4 || displayedUsers == null) && + state.isLoading && + (props.scope == "friends" || props.scope === "similar") && ( +

+ Follow More Users to Unlock More Personalized Recommendations, See + Who’s + + Trending + +

+ )} + {!props.sidebar && ( + + {state.userData.map((user, index) => ( + + + + ))} + + )} + {props.sidebar && ( + + {state.userData.map((user, index) => ( + + + + ))} + )} - + {state.currentPage < state.totalPages ? ( + + ) : null} +
); From d658f4b969fff61374741987639d4d7b067f1f1e Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Wed, 4 Oct 2023 19:41:19 +0200 Subject: [PATCH 14/33] fix() Engagement Tracking, remove wrapping payload object --- src/Recommender/Service/EngagementTracker.jsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Recommender/Service/EngagementTracker.jsx b/src/Recommender/Service/EngagementTracker.jsx index 9a0056c4..9c3abc80 100644 --- a/src/Recommender/Service/EngagementTracker.jsx +++ b/src/Recommender/Service/EngagementTracker.jsx @@ -1,10 +1,7 @@ -const dataplane = "https://neardanieossax.dataplane.rudderstack.com"; //test -// const dataplane = "https://near.dataplane.rudderstack.com"; //prod +const dataplane = "https://near.dataplane.rudderstack.com"; //prod const uri = "/v1/track"; const api_url = `${dataplane}${uri}`; -// const auth = "Basic MlVvMlBYSE9UdzJjUWRucThJUWJQTG9DOG5mOg=="; //test -const auth = "Basic MldJT1o0aFAwV3FKUnJKbUNsejFDek1RVVBqOg=="; //test2 -// const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod +const auth = "Basic MlVub3dMd2lXRnc3YzM1QU11RUVkREVJa2RvOg=="; //prod const generateAnonId = (length) => { const characters = @@ -21,7 +18,6 @@ const generateAnonId = (length) => { const trackEngagement = () => { const payload = { anonymousId: generateAnonId(24), - userId: "", channel: "web", context: props.fromContext, type: "track", @@ -29,15 +25,16 @@ const trackEngagement = () => { sentAt: new Date().toISOString(), event: props.event, properties: { + accountId: props.accountId, accountIdRank: props.accountIdRank, }, }; asyncFetch(api_url, { - body: JSON.stringify({ payload }), + body: JSON.stringify(payload), headers: { "Content-Type": "application/json", - Authorization: auth, + "Authorization": auth, }, method: "POST", }) From d8ed82f2b3da42bae0762ad0c376d18a90c759b5 Mon Sep 17 00:00:00 2001 From: scopalaffairs Date: Thu, 5 Oct 2023 13:10:01 +0200 Subject: [PATCH 15/33] feat() Added state for results < 4 --- src/Recommender/Views/FriendsOfFriends.jsx | 86 ++++++++++---------- src/Recommender/Views/SimilarProfiles.jsx | 87 +++++++++++---------- src/Recommender/Views/TrendingUsersView.jsx | 5 +- 3 files changed, 94 insertions(+), 84 deletions(-) diff --git a/src/Recommender/Views/FriendsOfFriends.jsx b/src/Recommender/Views/FriendsOfFriends.jsx index 098f8c0f..38b0bbf9 100644 --- a/src/Recommender/Views/FriendsOfFriends.jsx +++ b/src/Recommender/Views/FriendsOfFriends.jsx @@ -89,7 +89,6 @@ const getRecommendedUsers = (page) => { updateState(parsedResults.data, totalPageNum); } else { console.log( - res, "Error fetching data. Try reloading the page, or no data available." ); } @@ -100,7 +99,9 @@ const getRecommendedUsers = (page) => { const totalPageNum = parsedResults.total_pages || 10; updateState(parsedResults.data, totalPageNum); } else { - console.log(res, "Error fetching data. Try reloading the page."); + console.log( + "Error fetching data. Try reloading the page, or no data available." + ); } }); } @@ -123,47 +124,50 @@ if (state.isLoading) { return ( - {state.isLoading && displayedUsers.length == null &&

Loading...

} + {state.isLoading &&

Loading...

} {state.error &&

Error: {state.error}

} - {(displayedUsers.length < 4 || displayedUsers == null) && - state.isLoading && - (props.scope == "friends" || props.scope === "similar") && ( -

- Follow More Users to Unlock More Personalized Recommendations, See - Who’s - - Trending - -

- )} - {displayedUsers.map((user, index) => ( - - - - ))} + {!state.isLoading && displayedUsers.length > 2 ? ( + displayedUsers.map((user, index) => ( + + + + )) + ) : ( + <> + {!state.isLoading && ( +
+ Follow More Users to Unlock More Personalized Recommendations, See + Who’s + + Trending + +
+ )} + + )}
{!props.returnElements && state.currentPage < state.totalPages ? (