From b5057f4028c1e7a6be254049cc7f47f766093636 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Wed, 1 Jun 2022 11:31:46 -0600 Subject: [PATCH 01/14] Notifications ux fixes - wip (#383) * Notifications generating on comment,answer,contract update * Notifications MVP * Submitted an answer => answered * Listen for unseen notifications * Fix userlink formatting, move page * Fix links * Remove redundant code * Cleanup * Cleanup * Refactor name * Comments * Cleanup & update notif only after data retrieval * Find initial new notifs on user change * Enforce auth rules in db * eslint update * Code review changes * Refactor reason * Add todos * Show question in notifiation title * Allow larger width on md * Condense on mobile * Decrease padding, hide title on mobile * Line clamp notifications * Shrink text --- functions/src/create-notification.ts | 3 +- web/components/feed/copy-link-date-time.tsx | 8 +- web/pages/notifications.tsx | 90 ++++++++++++++------- 3 files changed, 69 insertions(+), 32 deletions(-) diff --git a/functions/src/create-notification.ts b/functions/src/create-notification.ts index 57f5d6ef..bd77f690 100644 --- a/functions/src/create-notification.ts +++ b/functions/src/create-notification.ts @@ -65,7 +65,8 @@ export const createNotification = async ( // TODO: Update for liquidity. // TODO: Find tagged users. // TODO: Find replies to comments. - // TODO: Filter bets for only open bets + // TODO: Filter bets for only open bets. + // TODO: Notify users of their own closed but not resolved contracts. if ( sourceType === 'comment' || sourceType === 'answer' || diff --git a/web/components/feed/copy-link-date-time.tsx b/web/components/feed/copy-link-date-time.tsx index 60395801..0ffac52e 100644 --- a/web/components/feed/copy-link-date-time.tsx +++ b/web/components/feed/copy-link-date-time.tsx @@ -8,13 +8,15 @@ import Link from 'next/link' import { fromNow } from 'web/lib/util/time' import { ToastClipboard } from 'web/components/toast-clipboard' import { LinkIcon } from '@heroicons/react/outline' +import clsx from 'clsx' export function CopyLinkDateTimeComponent(props: { contract: Contract createdTime: number elementId: string + className?: string }) { - const { contract, elementId, createdTime } = props + const { contract, elementId, createdTime, className } = props const [showToast, setShowToast] = useState(false) function copyLinkToComment( @@ -30,7 +32,7 @@ export function CopyLinkDateTimeComponent(props: { setTimeout(() => setShowToast(false), 2000) } return ( - <> +
- +
) } diff --git a/web/pages/notifications.tsx b/web/pages/notifications.tsx index b3c10a20..669f7a2b 100644 --- a/web/pages/notifications.tsx +++ b/web/pages/notifications.tsx @@ -1,7 +1,11 @@ import { Tabs } from 'web/components/layout/tabs' import { useUser } from 'web/hooks/use-user' import React, { useEffect, useState } from 'react' -import { Notification } from 'common/notification' +import { + Notification, + notification_reason_types, + notification_source_types, +} from 'common/notification' import { listenForNotifications } from 'web/lib/firebase/notifications' import { Avatar } from 'web/components/avatar' import { Row } from 'web/components/layout/row' @@ -15,9 +19,9 @@ import { Comment } from 'web/lib/firebase/comments' import { getValue } from 'web/lib/firebase/utils' import Custom404 from 'web/pages/404' import { UserLink } from 'web/components/user-page' -import { Linkify } from 'web/components/linkify' import { User } from 'common/user' import { useContract } from 'web/hooks/use-contract' +import { Contract } from 'common/contract' export default function Notifications() { const user = useUser() @@ -37,8 +41,8 @@ export default function Notifications() { // TODO: use infinite scroll return ( -
- + <div className={'p-2 sm:p-4'}> + <Title text={'Notifications'} className={'hidden sm:block'} /> <Tabs className={'pb-2 pt-1 '} defaultIndex={0} @@ -79,6 +83,7 @@ function Notification(props: { sourceUserName, sourceUserAvatarUrl, reasonText, + reason, sourceUserUsername, createdTime, } = notification @@ -140,7 +145,7 @@ function Notification(props: { } return ( - <div className={' bg-white px-4 pt-6'}> + <div className={'bg-white px-1 pt-6 text-sm sm:px-4'}> <Row className={'items-center text-gray-500 sm:justify-start'}> <Avatar avatarUrl={sourceUserAvatarUrl} @@ -148,35 +153,47 @@ function Notification(props: { className={'mr-2'} username={sourceUserName} /> - <div className={'flex-1'}> - <UserLink - name={sourceUserName || ''} - username={sourceUserUsername || ''} - className={'mr-0 flex-shrink-0'} - /> - <a href={getSourceUrl(sourceId)} className={'flex-1 pl-1'}> - {reasonText} - {contract && sourceId && ( - <div className={'inline'}> - <CopyLinkDateTimeComponent - contract={contract} - createdTime={createdTime} - elementId={getSourceIdForLinkComponent(sourceId)} - /> - </div> - )} - </a> + <div className={'flex-1 overflow-hidden sm:flex'}> + <div + className={ + 'flex max-w-sm shrink overflow-hidden text-ellipsis sm:max-w-md' + } + > + <UserLink + name={sourceUserName || ''} + username={sourceUserUsername || ''} + className={'mr-0 flex-shrink-0'} + /> + <a + href={getSourceUrl(sourceId)} + className={'inline-flex overflow-hidden text-ellipsis pl-1'} + > + {sourceType && reason ? ( + <div className={'inline truncate'}> + {getReasonTextFromReason(sourceType, reason, contract)} + </div> + ) : ( + reasonText + )} + </a> + </div> + {contract && sourceId && ( + <CopyLinkDateTimeComponent + contract={contract} + createdTime={createdTime} + elementId={getSourceIdForLinkComponent(sourceId)} + className={'-mx-1 inline-flex sm:inline-block'} + /> + )} </div> </Row> <a href={getSourceUrl(sourceId)}> - <div className={'ml-4 mt-1'}> + <div className={'mt-1 md:text-base'}> {' '} {contract && subText === contract.question ? ( - <div className={'text-md text-indigo-700 hover:underline'}> - {subText} - </div> + <div className={'text-indigo-700 hover:underline'}>{subText}</div> ) : ( - <Linkify text={subText} /> + <div className={'line-clamp-4 whitespace-pre-line'}>{subText}</div> )} </div> @@ -185,3 +202,20 @@ function Notification(props: { </div> ) } + +function getReasonTextFromReason( + source: notification_source_types, + reason: notification_reason_types, + contract: Contract | undefined +) { + switch (source) { + case 'comment': + return `commented on ${contract?.question}` + case 'contract': + return `${reason} ${contract?.question}` + case 'answer': + return `answered ${contract?.question}` + default: + return '' + } +} From 699b03eb420ac4f7fb657e22d4bc167ffafa9ccd Mon Sep 17 00:00:00 2001 From: Ian Philips <iansphilips@gmail.com> Date: Wed, 1 Jun 2022 11:52:27 -0600 Subject: [PATCH 02/14] Revert "Notifications ux fixes - wip (#383)" This reverts commit b5057f4028c1e7a6be254049cc7f47f766093636. --- functions/src/create-notification.ts | 3 +- web/components/feed/copy-link-date-time.tsx | 8 +- web/pages/notifications.tsx | 90 +++++++-------------- 3 files changed, 32 insertions(+), 69 deletions(-) diff --git a/functions/src/create-notification.ts b/functions/src/create-notification.ts index bd77f690..57f5d6ef 100644 --- a/functions/src/create-notification.ts +++ b/functions/src/create-notification.ts @@ -65,8 +65,7 @@ export const createNotification = async ( // TODO: Update for liquidity. // TODO: Find tagged users. // TODO: Find replies to comments. - // TODO: Filter bets for only open bets. - // TODO: Notify users of their own closed but not resolved contracts. + // TODO: Filter bets for only open bets if ( sourceType === 'comment' || sourceType === 'answer' || diff --git a/web/components/feed/copy-link-date-time.tsx b/web/components/feed/copy-link-date-time.tsx index 0ffac52e..60395801 100644 --- a/web/components/feed/copy-link-date-time.tsx +++ b/web/components/feed/copy-link-date-time.tsx @@ -8,15 +8,13 @@ import Link from 'next/link' import { fromNow } from 'web/lib/util/time' import { ToastClipboard } from 'web/components/toast-clipboard' import { LinkIcon } from '@heroicons/react/outline' -import clsx from 'clsx' export function CopyLinkDateTimeComponent(props: { contract: Contract createdTime: number elementId: string - className?: string }) { - const { contract, elementId, createdTime, className } = props + const { contract, elementId, createdTime } = props const [showToast, setShowToast] = useState(false) function copyLinkToComment( @@ -32,7 +30,7 @@ export function CopyLinkDateTimeComponent(props: { setTimeout(() => setShowToast(false), 2000) } return ( - <div className={clsx('inline', className)}> + <> <DateTimeTooltip time={createdTime}> <Link href={`/${contract.creatorUsername}/${contract.slug}#${elementId}`} @@ -55,6 +53,6 @@ export function CopyLinkDateTimeComponent(props: { </a> </Link> </DateTimeTooltip> - </div> + </> ) } diff --git a/web/pages/notifications.tsx b/web/pages/notifications.tsx index 669f7a2b..b3c10a20 100644 --- a/web/pages/notifications.tsx +++ b/web/pages/notifications.tsx @@ -1,11 +1,7 @@ import { Tabs } from 'web/components/layout/tabs' import { useUser } from 'web/hooks/use-user' import React, { useEffect, useState } from 'react' -import { - Notification, - notification_reason_types, - notification_source_types, -} from 'common/notification' +import { Notification } from 'common/notification' import { listenForNotifications } from 'web/lib/firebase/notifications' import { Avatar } from 'web/components/avatar' import { Row } from 'web/components/layout/row' @@ -19,9 +15,9 @@ import { Comment } from 'web/lib/firebase/comments' import { getValue } from 'web/lib/firebase/utils' import Custom404 from 'web/pages/404' import { UserLink } from 'web/components/user-page' +import { Linkify } from 'web/components/linkify' import { User } from 'common/user' import { useContract } from 'web/hooks/use-contract' -import { Contract } from 'common/contract' export default function Notifications() { const user = useUser() @@ -41,8 +37,8 @@ export default function Notifications() { // TODO: use infinite scroll return ( <Page> - <div className={'p-2 sm:p-4'}> - <Title text={'Notifications'} className={'hidden sm:block'} /> + <div className={'p-4'}> + <Title text={'Notifications'} /> <Tabs className={'pb-2 pt-1 '} defaultIndex={0} @@ -83,7 +79,6 @@ function Notification(props: { sourceUserName, sourceUserAvatarUrl, reasonText, - reason, sourceUserUsername, createdTime, } = notification @@ -145,7 +140,7 @@ function Notification(props: { } return ( - <div className={'bg-white px-1 pt-6 text-sm sm:px-4'}> + <div className={' bg-white px-4 pt-6'}> <Row className={'items-center text-gray-500 sm:justify-start'}> <Avatar avatarUrl={sourceUserAvatarUrl} @@ -153,47 +148,35 @@ function Notification(props: { className={'mr-2'} username={sourceUserName} /> - <div className={'flex-1 overflow-hidden sm:flex'}> - <div - className={ - 'flex max-w-sm shrink overflow-hidden text-ellipsis sm:max-w-md' - } - > - <UserLink - name={sourceUserName || ''} - username={sourceUserUsername || ''} - className={'mr-0 flex-shrink-0'} - /> - <a - href={getSourceUrl(sourceId)} - className={'inline-flex overflow-hidden text-ellipsis pl-1'} - > - {sourceType && reason ? ( - <div className={'inline truncate'}> - {getReasonTextFromReason(sourceType, reason, contract)} - </div> - ) : ( - reasonText - )} - </a> - </div> - {contract && sourceId && ( - <CopyLinkDateTimeComponent - contract={contract} - createdTime={createdTime} - elementId={getSourceIdForLinkComponent(sourceId)} - className={'-mx-1 inline-flex sm:inline-block'} - /> - )} + <div className={'flex-1'}> + <UserLink + name={sourceUserName || ''} + username={sourceUserUsername || ''} + className={'mr-0 flex-shrink-0'} + /> + <a href={getSourceUrl(sourceId)} className={'flex-1 pl-1'}> + {reasonText} + {contract && sourceId && ( + <div className={'inline'}> + <CopyLinkDateTimeComponent + contract={contract} + createdTime={createdTime} + elementId={getSourceIdForLinkComponent(sourceId)} + /> + </div> + )} + </a> </div> </Row> <a href={getSourceUrl(sourceId)}> - <div className={'mt-1 md:text-base'}> + <div className={'ml-4 mt-1'}> {' '} {contract && subText === contract.question ? ( - <div className={'text-indigo-700 hover:underline'}>{subText}</div> + <div className={'text-md text-indigo-700 hover:underline'}> + {subText} + </div> ) : ( - <div className={'line-clamp-4 whitespace-pre-line'}>{subText}</div> + <Linkify text={subText} /> )} </div> @@ -202,20 +185,3 @@ function Notification(props: { </div> ) } - -function getReasonTextFromReason( - source: notification_source_types, - reason: notification_reason_types, - contract: Contract | undefined -) { - switch (source) { - case 'comment': - return `commented on ${contract?.question}` - case 'contract': - return `${reason} ${contract?.question}` - case 'answer': - return `answered ${contract?.question}` - default: - return '' - } -} From 78a359407d2e0afcd4c7c2c124c38d6139f6ea30 Mon Sep 17 00:00:00 2001 From: Ian Philips <iansphilips@gmail.com> Date: Wed, 1 Jun 2022 12:26:41 -0600 Subject: [PATCH 03/14] Notifications reverted ux (#386) * Revert "Revert "Notifications ux fixes - wip (#383)"" This reverts commit 699b03eb420ac4f7fb657e22d4bc167ffafa9ccd. * <p>=><div> --- functions/src/create-notification.ts | 3 +- web/components/feed/copy-link-date-time.tsx | 8 +- web/components/feed/feed-comments.tsx | 4 +- web/pages/notifications.tsx | 90 ++++++++++++++------- 4 files changed, 71 insertions(+), 34 deletions(-) diff --git a/functions/src/create-notification.ts b/functions/src/create-notification.ts index 57f5d6ef..bd77f690 100644 --- a/functions/src/create-notification.ts +++ b/functions/src/create-notification.ts @@ -65,7 +65,8 @@ export const createNotification = async ( // TODO: Update for liquidity. // TODO: Find tagged users. // TODO: Find replies to comments. - // TODO: Filter bets for only open bets + // TODO: Filter bets for only open bets. + // TODO: Notify users of their own closed but not resolved contracts. if ( sourceType === 'comment' || sourceType === 'answer' || diff --git a/web/components/feed/copy-link-date-time.tsx b/web/components/feed/copy-link-date-time.tsx index 60395801..0ffac52e 100644 --- a/web/components/feed/copy-link-date-time.tsx +++ b/web/components/feed/copy-link-date-time.tsx @@ -8,13 +8,15 @@ import Link from 'next/link' import { fromNow } from 'web/lib/util/time' import { ToastClipboard } from 'web/components/toast-clipboard' import { LinkIcon } from '@heroicons/react/outline' +import clsx from 'clsx' export function CopyLinkDateTimeComponent(props: { contract: Contract createdTime: number elementId: string + className?: string }) { - const { contract, elementId, createdTime } = props + const { contract, elementId, createdTime, className } = props const [showToast, setShowToast] = useState(false) function copyLinkToComment( @@ -30,7 +32,7 @@ export function CopyLinkDateTimeComponent(props: { setTimeout(() => setShowToast(false), 2000) } return ( - <> + <div className={clsx('inline', className)}> <DateTimeTooltip time={createdTime}> <Link href={`/${contract.creatorUsername}/${contract.slug}#${elementId}`} @@ -53,6 +55,6 @@ export function CopyLinkDateTimeComponent(props: { </a> </Link> </DateTimeTooltip> - </> + </div> ) } diff --git a/web/components/feed/feed-comments.tsx b/web/components/feed/feed-comments.tsx index db55e890..3890bbe5 100644 --- a/web/components/feed/feed-comments.tsx +++ b/web/components/feed/feed-comments.tsx @@ -166,7 +166,7 @@ export function FeedComment(props: { avatarUrl={userAvatarUrl} /> <div className="min-w-0 flex-1"> - <p className="mt-0.5 pl-0.5 text-sm text-gray-500"> + <div className="mt-0.5 pl-0.5 text-sm text-gray-500"> <UserLink className="text-gray-500" username={userUsername} @@ -204,7 +204,7 @@ export function FeedComment(props: { createdTime={createdTime} elementId={comment.id} /> - </p> + </div> <TruncatedComment comment={text} moreHref={contractPath(contract)} diff --git a/web/pages/notifications.tsx b/web/pages/notifications.tsx index b3c10a20..63c9fc2f 100644 --- a/web/pages/notifications.tsx +++ b/web/pages/notifications.tsx @@ -1,7 +1,11 @@ import { Tabs } from 'web/components/layout/tabs' import { useUser } from 'web/hooks/use-user' import React, { useEffect, useState } from 'react' -import { Notification } from 'common/notification' +import { + Notification, + notification_reason_types, + notification_source_types, +} from 'common/notification' import { listenForNotifications } from 'web/lib/firebase/notifications' import { Avatar } from 'web/components/avatar' import { Row } from 'web/components/layout/row' @@ -15,9 +19,9 @@ import { Comment } from 'web/lib/firebase/comments' import { getValue } from 'web/lib/firebase/utils' import Custom404 from 'web/pages/404' import { UserLink } from 'web/components/user-page' -import { Linkify } from 'web/components/linkify' import { User } from 'common/user' import { useContract } from 'web/hooks/use-contract' +import { Contract } from 'common/contract' export default function Notifications() { const user = useUser() @@ -37,8 +41,8 @@ export default function Notifications() { // TODO: use infinite scroll return ( <Page> - <div className={'p-4'}> - <Title text={'Notifications'} /> + <div className={'p-2 sm:p-4'}> + <Title text={'Notifications'} className={'hidden md:block'} /> <Tabs className={'pb-2 pt-1 '} defaultIndex={0} @@ -79,6 +83,7 @@ function Notification(props: { sourceUserName, sourceUserAvatarUrl, reasonText, + reason, sourceUserUsername, createdTime, } = notification @@ -140,7 +145,7 @@ function Notification(props: { } return ( - <div className={' bg-white px-4 pt-6'}> + <div className={'bg-white px-1 pt-6 text-sm sm:px-4'}> <Row className={'items-center text-gray-500 sm:justify-start'}> <Avatar avatarUrl={sourceUserAvatarUrl} @@ -148,35 +153,47 @@ function Notification(props: { className={'mr-2'} username={sourceUserName} /> - <div className={'flex-1'}> - <UserLink - name={sourceUserName || ''} - username={sourceUserUsername || ''} - className={'mr-0 flex-shrink-0'} - /> - <a href={getSourceUrl(sourceId)} className={'flex-1 pl-1'}> - {reasonText} - {contract && sourceId && ( - <div className={'inline'}> - <CopyLinkDateTimeComponent - contract={contract} - createdTime={createdTime} - elementId={getSourceIdForLinkComponent(sourceId)} - /> - </div> - )} - </a> + <div className={'flex-1 overflow-hidden sm:flex'}> + <div + className={ + 'flex max-w-sm shrink overflow-hidden text-ellipsis sm:max-w-md' + } + > + <UserLink + name={sourceUserName || ''} + username={sourceUserUsername || ''} + className={'mr-0 flex-shrink-0'} + /> + <a + href={getSourceUrl(sourceId)} + className={'inline-flex overflow-hidden text-ellipsis pl-1'} + > + {sourceType && reason ? ( + <div className={'inline truncate'}> + {getReasonTextFromReason(sourceType, reason, contract)} + </div> + ) : ( + reasonText + )} + </a> + </div> + {contract && sourceId && ( + <CopyLinkDateTimeComponent + contract={contract} + createdTime={createdTime} + elementId={getSourceIdForLinkComponent(sourceId)} + className={'-mx-1 inline-flex sm:inline-block'} + /> + )} </div> </Row> <a href={getSourceUrl(sourceId)}> - <div className={'ml-4 mt-1'}> + <div className={'mt-1 md:text-base'}> {' '} {contract && subText === contract.question ? ( - <div className={'text-md text-indigo-700 hover:underline'}> - {subText} - </div> + <div className={'text-indigo-700 hover:underline'}>{subText}</div> ) : ( - <Linkify text={subText} /> + <div className={'line-clamp-4 whitespace-pre-line'}>{subText}</div> )} </div> @@ -185,3 +202,20 @@ function Notification(props: { </div> ) } + +function getReasonTextFromReason( + source: notification_source_types, + reason: notification_reason_types, + contract: Contract | undefined +) { + switch (source) { + case 'comment': + return `commented on ${contract?.question}` + case 'contract': + return `${reason} ${contract?.question}` + case 'answer': + return `answered ${contract?.question}` + default: + return '' + } +} From c2f993ddf25bd116d394a890481c147d9015b295 Mon Sep 17 00:00:00 2001 From: TrueMilli <61841994+TrueMilli@users.noreply.github.com> Date: Thu, 2 Jun 2022 01:30:36 +0200 Subject: [PATCH 04/14] check id on update (#393) --- firestore.rules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firestore.rules b/firestore.rules index feba35d9..e1e82089 100644 --- a/firestore.rules +++ b/firestore.rules @@ -45,7 +45,8 @@ service cloud.firestore { match /contracts/{contractId} { allow read; allow update: if request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']); + .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']) + && resource.data.id == request.auth.uid; allow update: if isAdmin(); } From f14bb63393bbd87e2558a18b539570dc740249ff Mon Sep 17 00:00:00 2001 From: James Grugett <jahooma@gmail.com> Date: Wed, 1 Jun 2022 18:30:40 -0500 Subject: [PATCH 05/14] Check that sold bet is by auth'd user --- functions/src/sell-bet.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/functions/src/sell-bet.ts b/functions/src/sell-bet.ts index 39ed8017..3ef5a094 100644 --- a/functions/src/sell-bet.ts +++ b/functions/src/sell-bet.ts @@ -50,6 +50,7 @@ export const sellBet = functions.runWith({ minInstances: 1 }).https.onCall( if (!betSnap.exists) return { status: 'error', message: 'Invalid bet' } const bet = betSnap.data() as Bet + if (userId !== bet.userId) return { status: 'error', message: 'Not authorized' } if (bet.isSold) return { status: 'error', message: 'Bet already sold' } const newBetDoc = firestore From 311b39ffe9f9546860276584fa4f938174fa5dab Mon Sep 17 00:00:00 2001 From: James Grugett <jahooma@gmail.com> Date: Wed, 1 Jun 2022 23:31:38 -0500 Subject: [PATCH 06/14] Demote charity to more menu --- web/components/nav/sidebar.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index 70396676..49bb0c0d 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -50,8 +50,6 @@ function getNavigation(username: string) { href: `/notifications`, icon: NotificationsIcon, }, - - { name: 'Charity', href: '/charity', icon: HeartIcon }, ] } @@ -62,6 +60,7 @@ function getMoreNavigation(user?: User | null) { if (!user) { return [ + { name: 'Charity', href: '/charity' }, { name: 'Leaderboards', href: '/leaderboards' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, { name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' }, @@ -70,6 +69,7 @@ function getMoreNavigation(user?: User | null) { return [ { name: 'Add funds', href: '/add-funds' }, + { name: 'Charity', href: '/charity' }, { name: 'Leaderboards', href: '/leaderboards' }, { name: 'Blog', href: 'https://news.manifold.markets' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, @@ -82,7 +82,6 @@ function getMoreNavigation(user?: User | null) { const signedOutNavigation = [ { name: 'Home', href: '/home', icon: HomeIcon }, { name: 'Explore', href: '/markets', icon: SearchIcon }, - { name: 'Charity', href: '/charity', icon: HeartIcon }, { name: 'About', href: 'https://docs.manifold.markets', icon: BookOpenIcon }, ] From c40f2904f0fcdf0a5faa7216ecbe2813cd86f1f9 Mon Sep 17 00:00:00 2001 From: James Grugett <jahooma@gmail.com> Date: Thu, 2 Jun 2022 10:42:05 -0500 Subject: [PATCH 07/14] Revert "check id on update (#393)" This reverts commit c2f993ddf25bd116d394a890481c147d9015b295. --- firestore.rules | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/firestore.rules b/firestore.rules index e1e82089..feba35d9 100644 --- a/firestore.rules +++ b/firestore.rules @@ -45,8 +45,7 @@ service cloud.firestore { match /contracts/{contractId} { allow read; allow update: if request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']) - && resource.data.id == request.auth.uid; + .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']); allow update: if isAdmin(); } From 31a0a378efc4ab30fb6c62297d3a2888ae9b9689 Mon Sep 17 00:00:00 2001 From: Ian Philips <iansphilips@gmail.com> Date: Thu, 2 Jun 2022 11:14:01 -0600 Subject: [PATCH 08/14] Sync firestore rules from firebase console --- firestore.rules | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/firestore.rules b/firestore.rules index feba35d9..f8cc148c 100644 --- a/firestore.rules +++ b/firestore.rules @@ -7,23 +7,28 @@ service cloud.firestore { function isAdmin() { return request.auth.uid == 'igi2zGXsfxYPgB0DJTXVJVmwCOr2' // Austin - || request.auth.uid == '5LZ4LgYuySdL1huCWe7bti02ghx2' // James - || request.auth.uid == 'tlmGNz9kjXc2EteizMORes4qvWl2' // Stephen - || request.auth.uid == 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // Manifold + || request.auth.uid == '5LZ4LgYuySdL1huCWe7bti02ghx2' // James + || request.auth.uid == 'tlmGNz9kjXc2EteizMORes4qvWl2' // Stephen + || request.auth.uid == 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // Manifold } match /users/{userId} { allow read; allow update: if resource.data.id == request.auth.uid - && request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories']); + && request.resource.data.diff(resource.data).affectedKeys() + .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories']); + } + + match /users/{userId}/follows/{followUserId} { + allow read; + allow write: if request.auth.uid == userId; } match /private-users/{userId} { allow read: if resource.data.id == request.auth.uid || isAdmin(); allow update: if (resource.data.id == request.auth.uid || isAdmin()) - && request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['apiKey']); + && request.resource.data.diff(resource.data).affectedKeys() + .hasOnly(['apiKey', 'unsubscribedFromResolutionEmails', 'unsubscribedFromCommentEmails', 'unsubscribedFromAnswerEmails', 'notificationPreferences' ]); } match /private-users/{userId}/views/{viewId} { @@ -45,7 +50,8 @@ service cloud.firestore { match /contracts/{contractId} { allow read; allow update: if request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']); + .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']) + && resource.data.creatorId == request.auth.uid; allow update: if isAdmin(); } @@ -88,5 +94,12 @@ service cloud.firestore { match /txns/{txnId} { allow read; } + + match /users/{userId}/notifications/{notificationId} { + allow read; + allow update: if resource.data.userId == request.auth.uid + && request.resource.data.diff(resource.data).affectedKeys() + .hasOnly(['isSeen', 'viewTime']); + } } } From 9b4974a3da4170e9f81c4e8a86e7f014d8f68848 Mon Sep 17 00:00:00 2001 From: Ian Philips <iansphilips@gmail.com> Date: Thu, 2 Jun 2022 11:23:25 -0600 Subject: [PATCH 09/14] Auth for description/close time, unauth for tags --- firestore.rules | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firestore.rules b/firestore.rules index f8cc148c..1dc4fd37 100644 --- a/firestore.rules +++ b/firestore.rules @@ -50,7 +50,9 @@ service cloud.firestore { match /contracts/{contractId} { allow read; allow update: if request.resource.data.diff(resource.data).affectedKeys() - .hasOnly(['description', 'closeTime', 'tags', 'lowercaseTags']) + .hasOnly(['tags', 'lowercaseTags']); + allow update: if request.resource.data.diff(resource.data).affectedKeys() + .hasOnly(['description', 'closeTime']) && resource.data.creatorId == request.auth.uid; allow update: if isAdmin(); } From e1b1f30c5592e203dd9899d0311d798ec33cd783 Mon Sep 17 00:00:00 2001 From: Sinclair Chen <abc.sinclair@gmail.com> Date: Thu, 2 Jun 2022 10:56:56 -0700 Subject: [PATCH 10/14] make sell button show on mobile (bet list) --- web/components/bets-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx index 2d3380ca..f4e5d419 100644 --- a/web/components/bets-list.tsx +++ b/web/components/bets-list.tsx @@ -392,7 +392,7 @@ export function BetsSummary(props: { return ( <Row className={clsx('flex-wrap gap-4 sm:flex-nowrap sm:gap-6', className)}> - <Row className="gap-4 sm:gap-6"> + <Row className="flex-wrap gap-4 sm:gap-6"> {!isCpmm && ( <Col> <div className="whitespace-nowrap text-sm text-gray-500"> From 4fbc7703ce529734375897c5f71fd5da4b347e45 Mon Sep 17 00:00:00 2001 From: mantikoros <sgrugett@gmail.com> Date: Thu, 2 Jun 2022 14:27:01 -0500 Subject: [PATCH 11/14] create page: remove numeric markets --- web/pages/create.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/web/pages/create.tsx b/web/pages/create.tsx index 342eafeb..e8d28083 100644 --- a/web/pages/create.tsx +++ b/web/pages/create.tsx @@ -174,21 +174,16 @@ export function NewContract(props: { question: string; tag?: string }) { <ChoicesToggleGroup currentChoice={outcomeType} setChoice={(choice) => { - if (choice === 'NUMERIC') - setMarketInfoText( - 'Numeric markets are still experimental and subject to major revisions.' - ) - else if (choice === 'FREE_RESPONSE') + if (choice === 'FREE_RESPONSE') setMarketInfoText( 'Users can submit their own answers to this market.' ) else setMarketInfoText('') - setOutcomeType(choice as outcomeType) + setOutcomeType(choice as 'BINARY' | 'FREE_RESPONSE') }} choicesMap={{ 'Yes / No': 'BINARY', 'Free response': 'FREE_RESPONSE', - Numeric: 'NUMERIC', }} isSubmitting={isSubmitting} className={'col-span-4'} From 5b142baa981c0920ee948406fa031baf04ec2128 Mon Sep 17 00:00:00 2001 From: Austin Chen <akrolsmir@gmail.com> Date: Thu, 2 Jun 2022 13:35:28 -0700 Subject: [PATCH 12/14] Revert "Demote charity to more menu" This reverts commit 311b39ffe9f9546860276584fa4f938174fa5dab. --- web/components/nav/sidebar.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/components/nav/sidebar.tsx b/web/components/nav/sidebar.tsx index 49bb0c0d..70396676 100644 --- a/web/components/nav/sidebar.tsx +++ b/web/components/nav/sidebar.tsx @@ -50,6 +50,8 @@ function getNavigation(username: string) { href: `/notifications`, icon: NotificationsIcon, }, + + { name: 'Charity', href: '/charity', icon: HeartIcon }, ] } @@ -60,7 +62,6 @@ function getMoreNavigation(user?: User | null) { if (!user) { return [ - { name: 'Charity', href: '/charity' }, { name: 'Leaderboards', href: '/leaderboards' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, { name: 'Twitter', href: 'https://twitter.com/ManifoldMarkets' }, @@ -69,7 +70,6 @@ function getMoreNavigation(user?: User | null) { return [ { name: 'Add funds', href: '/add-funds' }, - { name: 'Charity', href: '/charity' }, { name: 'Leaderboards', href: '/leaderboards' }, { name: 'Blog', href: 'https://news.manifold.markets' }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh' }, @@ -82,6 +82,7 @@ function getMoreNavigation(user?: User | null) { const signedOutNavigation = [ { name: 'Home', href: '/home', icon: HomeIcon }, { name: 'Explore', href: '/markets', icon: SearchIcon }, + { name: 'Charity', href: '/charity', icon: HeartIcon }, { name: 'About', href: 'https://docs.manifold.markets', icon: BookOpenIcon }, ] From 5af2e1fef07a3e49cf3d8ebbb7c5d9b03c225e9e Mon Sep 17 00:00:00 2001 From: mantikoros <sgrugett@gmail.com> Date: Thu, 2 Jun 2022 15:53:56 -0500 Subject: [PATCH 13/14] up arrows green, down arrows red --- web/components/contract/quick-bet.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/web/components/contract/quick-bet.tsx b/web/components/contract/quick-bet.tsx index 4c0e2ce5..34eac6f4 100644 --- a/web/components/contract/quick-bet.tsx +++ b/web/components/contract/quick-bet.tsx @@ -68,8 +68,6 @@ export function QuickBet(props: { contract: Contract; user: User }) { // Catch any errors from hovering on an invalid option } - const color = getColor(contract, previewProb) - async function placeQuickBet(direction: 'UP' | 'DOWN') { const betPromise = async () => { const outcome = quickOutcome(contract, direction) @@ -128,14 +126,14 @@ export function QuickBet(props: { contract: Contract; user: User }) { <TriangleFillIcon className={clsx( 'mx-auto h-5 w-5', - upHover ? `text-${color}` : 'text-gray-400' + upHover ? 'text-green-500' : 'text-gray-400' )} /> ) : ( <TriangleFillIcon className={clsx( 'mx-auto h-5 w-5', - upHover ? `text-${color}` : 'text-gray-200' + upHover ? 'text-green-500' : 'text-gray-200' )} /> )} @@ -155,14 +153,14 @@ export function QuickBet(props: { contract: Contract; user: User }) { <TriangleDownFillIcon className={clsx( 'mx-auto h-5 w-5', - downHover ? `text-${color}` : 'text-gray-400' + downHover ? 'text-red-500' : 'text-gray-400' )} /> ) : ( <TriangleDownFillIcon className={clsx( 'mx-auto h-5 w-5', - downHover ? `text-${color}` : 'text-gray-200' + downHover ? 'text-red-500' : 'text-gray-200' )} /> )} From 5a4bb65d8057bc67b7630d4a8461f1906dfe402a Mon Sep 17 00:00:00 2001 From: mantikoros <sgrugett@gmail.com> Date: Thu, 2 Jun 2022 16:00:38 -0500 Subject: [PATCH 14/14] remove extra space (to fix prettier static check) --- web/components/contract/quick-bet.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/components/contract/quick-bet.tsx b/web/components/contract/quick-bet.tsx index 34eac6f4..8f642656 100644 --- a/web/components/contract/quick-bet.tsx +++ b/web/components/contract/quick-bet.tsx @@ -160,7 +160,7 @@ export function QuickBet(props: { contract: Contract; user: User }) { <TriangleDownFillIcon className={clsx( 'mx-auto h-5 w-5', - downHover ? 'text-red-500' : 'text-gray-200' + downHover ? 'text-red-500' : 'text-gray-200' )} /> )} @@ -269,10 +269,15 @@ export function getColor(contract: Contract, previewProb?: number) { 'primary' ) } + if (contract.outcomeType === 'NUMERIC') { return 'blue-400' } + if (contract.outcomeType === 'FREE_RESPONSE') { + return 'blue-400' + } + const marketClosed = (contract.closeTime || Infinity) < Date.now() const prob = previewProb ?? getProb(contract) return marketClosed ? 'gray-400' : prob >= 0.5 ? 'primary' : 'red-400'