diff --git a/components/Activity.tsx b/components/Activity.tsx index f8a0bab..7f7b16d 100644 --- a/components/Activity.tsx +++ b/components/Activity.tsx @@ -70,14 +70,14 @@ const Activity = ({ updates, pageUser, onClickDate }: { updates: { date: string {/* get the years that the user has written updates and display them as tabs above */} {years.map(y => ( ))} diff --git a/components/ActivityGrid.tsx b/components/ActivityGrid.tsx index 80b33b7..a52a382 100644 --- a/components/ActivityGrid.tsx +++ b/components/ActivityGrid.tsx @@ -20,11 +20,11 @@ const GridLabel = ({ row, col, children }: { row: number, col: number, children: gridColumn: col, fontSize: 10, }} - className="text-stone-300 dark:text-stone-700" + className="text-neutral-300 dark:text-neutral-700" >{children} ) -export default function ActivityGrid({ data, label, color, onClickDate }: { data: ActivityDayMap, label?: string, color?: string, onClickDate: (date: string) => void }) { +export default function ActivityGrid({ data, onClickDate }: { data: ActivityDayMap, onClickDate: (date: string) => void }) { const numCols = 53; const monthChangeDays: ActivityDay[] = Object.values(data).filter((d, i, a) => ( @@ -58,7 +58,7 @@ export default function ActivityGrid({ data, label, color, onClickDate }: { data gridRow: dateActivity.day + 2, gridColumn: dateActivity.week + 2, }} - className={classNames(dateActivity.count > 0 ? "bg-tblue cursor-pointer" : "bg-gray-100", "hover:!opacity-100 w-[13px] h-[13px] rounded-[3px]")} + className={classNames(dateActivity.count > 0 ? "bg-tblue cursor-pointer" : "bg-gray-100 dark:bg-neutral-800", "hover:!opacity-100 w-[13px] h-[13px] rounded-[3px]")} key={format(dateActivity.date, "yyyy-MM-dd")} onClick={() => { if (dateActivity.count > 0) diff --git a/components/CustomSelect.tsx b/components/CustomSelect.tsx index d3067d8..ead095f 100644 --- a/components/CustomSelect.tsx +++ b/components/CustomSelect.tsx @@ -1,60 +1,62 @@ import { useTheme } from 'next-themes'; import Select from 'react-select' -export default function CustomSelect(props) { - const { theme, setTheme } = useTheme(); - const customStyles = { - option: (provided, state) => { - const optionBackgroundColor = state.isSelected - ? 'rgb(38, 132, 255)' /* default */ - : theme === 'dark' - ? '#000' - : '#FFF'; - - return { - ...provided, - padding: 8, - paddingRight: 16, - paddingLeft: 16, - backgroundColor: optionBackgroundColor, - - ':hover': { - backgroundColor: state.isSelected - ? optionBackgroundColor - : theme === 'dark' - ? 'rgba(243, 244, 246, 0.2)' - : 'rgba(243, 244, 246, 1)', // tailwind gray 100, just like moremenu on hover - }, - }; - }, - - valueContainer: provided => ({ +export const getCustomStyles = (theme) => ({ + option: (provided, state) => { + const optionBackgroundColor = state.isSelected + ? 'rgb(38, 132, 255)' /* default */ + : theme === 'dark' + ? '#000' + : '#FFF'; + + return { ...provided, padding: 8, paddingRight: 16, paddingLeft: 16, - }), - - control: provided => ({ - ...provided, - borderColor: '#e5e7eb', - backgroundColor: theme === 'dark' ? 'rgba(0, 0, 0, 0)' : '#FFF', - }), - - container: provided => ({ - ...provided, - marginBottom: 8, - }), + backgroundColor: optionBackgroundColor, + + ':hover': { + backgroundColor: state.isSelected + ? optionBackgroundColor + : theme === 'dark' + ? 'rgba(243, 244, 246, 0.2)' + : 'rgba(243, 244, 246, 1)', // tailwind gray 100, just like moremenu on hover + }, + }; + }, + + valueContainer: provided => ({ + ...provided, + padding: 8, + paddingRight: 16, + paddingLeft: 16, + }), + + control: provided => ({ + ...provided, + borderColor: '#e5e7eb', + backgroundColor: theme === 'dark' ? 'rgba(0, 0, 0, 0)' : '#FFF', + }), + + container: provided => ({ + ...provided, + marginBottom: 8, + }), + + menu: provided => ({ + ...provided, + backgroundColor: theme === 'dark' ? '#000' : '#FFF', + }), + + singleValue: provided => ({ + ...provided, + color: theme === 'dark' ? '#FFF' : '#000', + }), +}); - menu: provided => ({ - ...provided, - backgroundColor: theme === 'dark' ? '#000' : '#FFF', - }), - - singleValue: provided => ({ - ...provided, - color: theme === 'dark' ? '#FFF' : '#000', - }), - }; - return } diff --git a/components/EditUpdate.tsx b/components/EditUpdate.tsx index 492e5b4..f748ef1 100644 --- a/components/EditUpdate.tsx +++ b/components/EditUpdate.tsx @@ -5,6 +5,8 @@ import {User} from "../utils/types"; import axios from "axios"; import MentionItem from "./MentionItem"; import Creatable from "react-select/creatable"; +import { getCustomStyles } from "./CustomSelect"; +import { useTheme } from "next-themes"; function getMentionFromCM(instance) { const cursorInfo = instance.getCursor(); @@ -110,6 +112,8 @@ export default function EditUpdate({body, setBody, title, setTitle, date, setDat return () => editorEl.removeEventListener("keydown", keydownHandler); }, [editorRef.current, mentionOpen, mentionQuery, userSelectedIndex, userList]); + + const { theme, setTheme } = useTheme(); return ( <> @@ -141,8 +145,8 @@ export default function EditUpdate({body, setBody, title, setTitle, date, setDat
Tags (optional)
- ({ value: d, label: d }))} isMulti={true} isClearable={true} - onChange={option => setTags(option.map(d => d.value))} defaultValue={tags.map(d => ({value: d, label: d}))} /> + ({ value: d, label: d }))} isMulti={true} isClearable={true} className="z-10 relative" + onChange={option => setTags(option.map(d => d.value))} defaultValue={tags.map(d => ({value: d, label: d}))} styles={{...getCustomStyles(theme), multiValueRemove: (styles) => ({...styles, color: "black"})}}/>
diff --git a/components/UpdateFeed.tsx b/components/UpdateFeed.tsx index e3d8ff5..d6b7d69 100644 --- a/components/UpdateFeed.tsx +++ b/components/UpdateFeed.tsx @@ -45,7 +45,7 @@ export default function UpdateFeed({updates, page, setPage, count}: {updates: Fe {wordsCount(update.body)} word{wordsCount(update.body) > 1 ? "s" : ""}

{update.tags && update.tags.map(tag => ( -
#{tag}
+
#{tag}
))} diff --git a/package.json b/package.json index 2bd3d5c..b83dcca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "updately", - "version": "0.11.2", + "version": "0.11.3", "private": true, "license": "BSD-3-Clause", "scripts": { diff --git a/pages/[username]/[updateUrl].tsx b/pages/[username]/[updateUrl].tsx index d82055c..ebf92ab 100644 --- a/pages/[username]/[updateUrl].tsx +++ b/pages/[username]/[updateUrl].tsx @@ -21,6 +21,7 @@ import {FiHeart} from "react-icons/fi"; import {notificationModel} from "../../models/models"; import {getMentionsAndBodySegments} from "../../components/UpdateCommentItem"; import { DeleteModal } from "../../components/Modal"; +import { ssrRedirect } from "next-response-helpers"; export default function UpdatePage(props: { data: GetUpdateRequestResponse, updateUrl: string, userData: User }) { const router = useRouter(); @@ -171,7 +172,7 @@ export default function UpdatePage(props: { data: GetUpdateRequestResponse, upda {!!tags.length && (
{tags.map(d => ( - #{d} + #{d} ))}
)} @@ -288,7 +289,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { // or are the user data.user._id.toString() === userData._id.toString() ) - )) return { notFound: true }; + )) return ssrRedirect(`/@${data.user.urlName}?privateredirect=true`); if (userData) await notificationModel.updateMany({userId: userData._id, updateId: data.update._id}, {read: true}); diff --git a/pages/[username]/index.tsx b/pages/[username]/index.tsx index 5fc3fd4..81aa0bb 100644 --- a/pages/[username]/index.tsx +++ b/pages/[username]/index.tsx @@ -83,12 +83,19 @@ export default function UserProfile(props: { user: UserAgg, userData: User, foll const isProfilePrivateToLoggedInUser = (pageUser.private || pageUser.truePrivate) && (!userData || !pageUser.followers.includes(props.userData.email) && !isOwner); + const isPrivateUpdateRedirect = !!router.query.privateredirect; + return (
+ {isPrivateUpdateRedirect && ( +
+

You accessed a link to an update on a private account that you do not follow. Follow the account to view the update.

+
+ )}
@@ -245,7 +252,7 @@ export default function UserProfile(props: { user: UserAgg, userData: User, foll

{update.tags && update.tags.map(tag => ( - + ))}
diff --git a/pages/[username]/random.tsx b/pages/[username]/random.tsx new file mode 100644 index 0000000..16510ce --- /dev/null +++ b/pages/[username]/random.tsx @@ -0,0 +1,56 @@ +import { GetServerSideProps } from "next"; +import { getSession } from "next-auth/react"; +import { userModel } from "../../models/models"; +import getLookup from "../../utils/getLookup"; +import { ssrRedirect } from "next-response-helpers"; + +export default function Random() { + return ( + <> + ) +} + +export const getServerSideProps: GetServerSideProps = async (context) => { + if (Array.isArray(context.params.username) || context.params.username.substring(0, 1) !== "@") return { notFound: true }; + + const username: string = context.params.username.substring(1); + + const pageUserArr = await userModel.aggregate([ + {$match: {urlName: username}}, + getLookup("users", "_id", "following", "followingArr"), + getLookup("users", "email", "followers", "followersArr"), + { + $lookup: { + from: "updates", + as: "updatesArr", + let: {userId: "$_id"}, + pipeline: [ + {$match: {$expr: {$eq: ["$userId", "$$userId"]}}}, + {$sample: {size: 1}}, + ], + } + } + ]); + + const pageUser = pageUserArr[0]; + + console.log(pageUser); + + if (!pageUser) return {notFound: true}; + + const session = await getSession(context); + const thisUser = session ? await userModel.findOne({email: session.user.email}) : null; + + const isPrivate = (pageUser.truePrivate || pageUser.private); + + const canAccess = (!isPrivate || (thisUser && ( + // following user + pageUser.followers.includes(thisUser.email) || + // or are the user + pageUser._id.toString() === thisUser._id.toString() + ))); + + if (!canAccess || !pageUser.updatesArr.length) return ssrRedirect(`/@${username}`); + + return ssrRedirect(`/@${username}/${pageUser.updatesArr[0].url}`); +} \ No newline at end of file diff --git a/utils/requests.ts b/utils/requests.ts index 93a6e5c..da2d7aa 100644 --- a/utils/requests.ts +++ b/utils/requests.ts @@ -23,8 +23,6 @@ export async function getUpdateRequest(username: string, url: string): Promise