From ee3f89205341ab917869823c610132f943e3e193 Mon Sep 17 00:00:00 2001 From: SatsAllDay <128755788+SatsAllDay@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:29:55 -0400 Subject: [PATCH] Add new user setting to hide bookmarks from other users (and anon) (#424) * Add new setting to hide bookmarks from other users (and anon) * Optional chaining in case user doesnt exist, and avoid duplicate sql query in some cases --- api/resolvers/item.js | 17 ++++++++++++++++- api/typeDefs/user.js | 3 ++- fragments/users.js | 5 +++-- lib/validate.js | 5 +++-- pages/settings.js | 8 +++++++- .../20230818184016_hide_bookmarks/migration.sql | 3 +++ prisma/schema.prisma | 1 + 7 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 prisma/migrations/20230818184016_hide_bookmarks/migration.sql diff --git a/api/resolvers/item.js b/api/resolvers/item.js index 21479bd0..9de6f21e 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -314,6 +314,21 @@ export default { const decodedCursor = decodeCursor(cursor) let items, user, pins, subFull, table + // special authorization for bookmarks depending on owning users' privacy settings + if (type === 'bookmarks' && name && me?.name !== name) { + // the calling user is either not logged in, or not the user upon which the query is made, + // so we need to check authz + user = await models.user.findUnique({ where: { name } }) + if (user?.hideBookmarks) { + // early return with no results if bookmarks are hidden + return { + cursor: null, + items: [], + pins: [] + } + } + } + // HACK we want to optionally include the subName in the query // but the query planner doesn't like unused parameters const subArr = sub ? [sub] : [] @@ -324,7 +339,7 @@ export default { throw new GraphQLError('must supply name', { extensions: { code: 'BAD_INPUT' } }) } - user = await models.user.findUnique({ where: { name } }) + user ??= await models.user.findUnique({ where: { name } }) if (!user) { throw new GraphQLError('no user has that name', { extensions: { code: 'BAD_INPUT' } }) } diff --git a/api/typeDefs/user.js b/api/typeDefs/user.js index 975cd987..f19edfce 100644 --- a/api/typeDefs/user.js +++ b/api/typeDefs/user.js @@ -24,7 +24,7 @@ export default gql` noteEarning: Boolean!, noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!, noteInvites: Boolean!, noteJobIndicator: Boolean!, noteCowboyHat: Boolean!, hideInvoiceDesc: Boolean!, hideFromTopUsers: Boolean!, hideCowboyHat: Boolean!, clickToLoadImg: Boolean!, - wildWestMode: Boolean!, greeterMode: Boolean!, nostrPubkey: String, nostrRelays: [String!]): User + wildWestMode: Boolean!, greeterMode: Boolean!, nostrPubkey: String, nostrRelays: [String!], hideBookmarks: Boolean!): User setPhoto(photoId: ID!): Int! upsertBio(bio: String!): User! setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean @@ -79,6 +79,7 @@ export default gql` hideInvoiceDesc: Boolean! hideFromTopUsers: Boolean! hideCowboyHat: Boolean! + hideBookmarks: Boolean! clickToLoadImg: Boolean! wildWestMode: Boolean! greeterMode: Boolean! diff --git a/fragments/users.js b/fragments/users.js index 994de9e7..f2583aa9 100644 --- a/fragments/users.js +++ b/fragments/users.js @@ -52,6 +52,7 @@ export const SETTINGS_FIELDS = gql` hideInvoiceDesc hideFromTopUsers hideCowboyHat + hideBookmarks clickToLoadImg nostrPubkey nostrRelays @@ -81,13 +82,13 @@ mutation setSettings($tipDefault: Int!, $turboTipping: Boolean!, $fiatCurrency: $noteEarning: Boolean!, $noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!, $noteInvites: Boolean!, $noteJobIndicator: Boolean!, $noteCowboyHat: Boolean!, $hideInvoiceDesc: Boolean!, $hideFromTopUsers: Boolean!, $hideCowboyHat: Boolean!, $clickToLoadImg: Boolean!, - $wildWestMode: Boolean!, $greeterMode: Boolean!, $nostrPubkey: String, $nostrRelays: [String!]) { + $wildWestMode: Boolean!, $greeterMode: Boolean!, $nostrPubkey: String, $nostrRelays: [String!], $hideBookmarks: Boolean!) { setSettings(tipDefault: $tipDefault, turboTipping: $turboTipping, fiatCurrency: $fiatCurrency, noteItemSats: $noteItemSats, noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants, noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites, noteJobIndicator: $noteJobIndicator, noteCowboyHat: $noteCowboyHat, hideInvoiceDesc: $hideInvoiceDesc, hideFromTopUsers: $hideFromTopUsers, hideCowboyHat: $hideCowboyHat, clickToLoadImg: $clickToLoadImg, - wildWestMode: $wildWestMode, greeterMode: $greeterMode, nostrPubkey: $nostrPubkey, nostrRelays: $nostrRelays) { + wildWestMode: $wildWestMode, greeterMode: $greeterMode, nostrPubkey: $nostrPubkey, nostrRelays: $nostrRelays, hideBookmarks: $hideBookmarks) { ...SettingsFields } } diff --git a/lib/validate.js b/lib/validate.js index c1c14c2a..7699f234 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -1,4 +1,4 @@ -import { string, ValidationError, number, object, array, addMethod } from 'yup' +import { string, ValidationError, number, object, array, addMethod, boolean } from 'yup' import { BOOST_MIN, MAX_POLL_CHOICE_LENGTH, MAX_TITLE_LENGTH, MAX_POLL_NUM_CHOICES, MIN_POLL_NUM_CHOICES, SUBS_NO_JOBS } from './constants' import { NAME_QUERY } from '../fragments/users' import { URL_REGEXP, WS_REGEXP } from './url' @@ -203,7 +203,8 @@ export const settingsSchema = object({ nostrRelays: array().of( string().matches(WS_REGEXP, 'invalid web socket address') ).max(NOSTR_MAX_RELAY_NUM, - ({ max, value }) => `${Math.abs(max - value.length)} too many`) + ({ max, value }) => `${Math.abs(max - value.length)} too many`), + hideBookmarks: boolean() }) const warningMessage = 'If I logout, even accidentally, I will never be able to access my account again' diff --git a/pages/settings.js b/pages/settings.js index 14f8fdaa..63320410 100644 --- a/pages/settings.js +++ b/pages/settings.js @@ -73,7 +73,8 @@ export default function Settings ({ ssrData }) { wildWestMode: settings?.wildWestMode, greeterMode: settings?.greeterMode, nostrPubkey: settings?.nostrPubkey ? bech32encode(settings.nostrPubkey) : '', - nostrRelays: settings?.nostrRelays?.length ? settings?.nostrRelays : [''] + nostrRelays: settings?.nostrRelays?.length ? settings?.nostrRelays : [''], + hideBookmarks: settings?.hideBookmarks }} schema={settingsSchema} onSubmit={async ({ tipDefault, nostrPubkey, nostrRelays, ...values }) => { @@ -221,6 +222,11 @@ export default function Settings ({ ssrData }) { click to load external images} name='clickToLoadImg' + groupClassName='mb-0' + /> + hide my bookmarks from other stackers} + name='hideBookmarks' />
content