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
This commit is contained in:
parent
427fc9aeec
commit
ee3f892053
|
@ -314,6 +314,21 @@ export default {
|
||||||
const decodedCursor = decodeCursor(cursor)
|
const decodedCursor = decodeCursor(cursor)
|
||||||
let items, user, pins, subFull, table
|
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
|
// HACK we want to optionally include the subName in the query
|
||||||
// but the query planner doesn't like unused parameters
|
// but the query planner doesn't like unused parameters
|
||||||
const subArr = sub ? [sub] : []
|
const subArr = sub ? [sub] : []
|
||||||
|
@ -324,7 +339,7 @@ export default {
|
||||||
throw new GraphQLError('must supply name', { extensions: { code: 'BAD_INPUT' } })
|
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) {
|
if (!user) {
|
||||||
throw new GraphQLError('no user has that name', { extensions: { code: 'BAD_INPUT' } })
|
throw new GraphQLError('no user has that name', { extensions: { code: 'BAD_INPUT' } })
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default gql`
|
||||||
noteEarning: Boolean!, noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!,
|
noteEarning: Boolean!, noteAllDescendants: Boolean!, noteMentions: Boolean!, noteDeposits: Boolean!,
|
||||||
noteInvites: Boolean!, noteJobIndicator: Boolean!, noteCowboyHat: Boolean!, hideInvoiceDesc: Boolean!,
|
noteInvites: Boolean!, noteJobIndicator: Boolean!, noteCowboyHat: Boolean!, hideInvoiceDesc: Boolean!,
|
||||||
hideFromTopUsers: Boolean!, hideCowboyHat: Boolean!, clickToLoadImg: 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!
|
setPhoto(photoId: ID!): Int!
|
||||||
upsertBio(bio: String!): User!
|
upsertBio(bio: String!): User!
|
||||||
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean
|
setWalkthrough(tipPopover: Boolean, upvotePopover: Boolean): Boolean
|
||||||
|
@ -79,6 +79,7 @@ export default gql`
|
||||||
hideInvoiceDesc: Boolean!
|
hideInvoiceDesc: Boolean!
|
||||||
hideFromTopUsers: Boolean!
|
hideFromTopUsers: Boolean!
|
||||||
hideCowboyHat: Boolean!
|
hideCowboyHat: Boolean!
|
||||||
|
hideBookmarks: Boolean!
|
||||||
clickToLoadImg: Boolean!
|
clickToLoadImg: Boolean!
|
||||||
wildWestMode: Boolean!
|
wildWestMode: Boolean!
|
||||||
greeterMode: Boolean!
|
greeterMode: Boolean!
|
||||||
|
|
|
@ -52,6 +52,7 @@ export const SETTINGS_FIELDS = gql`
|
||||||
hideInvoiceDesc
|
hideInvoiceDesc
|
||||||
hideFromTopUsers
|
hideFromTopUsers
|
||||||
hideCowboyHat
|
hideCowboyHat
|
||||||
|
hideBookmarks
|
||||||
clickToLoadImg
|
clickToLoadImg
|
||||||
nostrPubkey
|
nostrPubkey
|
||||||
nostrRelays
|
nostrRelays
|
||||||
|
@ -81,13 +82,13 @@ mutation setSettings($tipDefault: Int!, $turboTipping: Boolean!, $fiatCurrency:
|
||||||
$noteEarning: Boolean!, $noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!,
|
$noteEarning: Boolean!, $noteAllDescendants: Boolean!, $noteMentions: Boolean!, $noteDeposits: Boolean!,
|
||||||
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $noteCowboyHat: Boolean!, $hideInvoiceDesc: Boolean!,
|
$noteInvites: Boolean!, $noteJobIndicator: Boolean!, $noteCowboyHat: Boolean!, $hideInvoiceDesc: Boolean!,
|
||||||
$hideFromTopUsers: Boolean!, $hideCowboyHat: Boolean!, $clickToLoadImg: 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,
|
setSettings(tipDefault: $tipDefault, turboTipping: $turboTipping, fiatCurrency: $fiatCurrency,
|
||||||
noteItemSats: $noteItemSats, noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants,
|
noteItemSats: $noteItemSats, noteEarning: $noteEarning, noteAllDescendants: $noteAllDescendants,
|
||||||
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites,
|
noteMentions: $noteMentions, noteDeposits: $noteDeposits, noteInvites: $noteInvites,
|
||||||
noteJobIndicator: $noteJobIndicator, noteCowboyHat: $noteCowboyHat, hideInvoiceDesc: $hideInvoiceDesc,
|
noteJobIndicator: $noteJobIndicator, noteCowboyHat: $noteCowboyHat, hideInvoiceDesc: $hideInvoiceDesc,
|
||||||
hideFromTopUsers: $hideFromTopUsers, hideCowboyHat: $hideCowboyHat, clickToLoadImg: $clickToLoadImg,
|
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
|
...SettingsFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { 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 { NAME_QUERY } from '../fragments/users'
|
||||||
import { URL_REGEXP, WS_REGEXP } from './url'
|
import { URL_REGEXP, WS_REGEXP } from './url'
|
||||||
|
@ -203,7 +203,8 @@ export const settingsSchema = object({
|
||||||
nostrRelays: array().of(
|
nostrRelays: array().of(
|
||||||
string().matches(WS_REGEXP, 'invalid web socket address')
|
string().matches(WS_REGEXP, 'invalid web socket address')
|
||||||
).max(NOSTR_MAX_RELAY_NUM,
|
).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'
|
const warningMessage = 'If I logout, even accidentally, I will never be able to access my account again'
|
||||||
|
|
|
@ -73,7 +73,8 @@ export default function Settings ({ ssrData }) {
|
||||||
wildWestMode: settings?.wildWestMode,
|
wildWestMode: settings?.wildWestMode,
|
||||||
greeterMode: settings?.greeterMode,
|
greeterMode: settings?.greeterMode,
|
||||||
nostrPubkey: settings?.nostrPubkey ? bech32encode(settings.nostrPubkey) : '',
|
nostrPubkey: settings?.nostrPubkey ? bech32encode(settings.nostrPubkey) : '',
|
||||||
nostrRelays: settings?.nostrRelays?.length ? settings?.nostrRelays : ['']
|
nostrRelays: settings?.nostrRelays?.length ? settings?.nostrRelays : [''],
|
||||||
|
hideBookmarks: settings?.hideBookmarks
|
||||||
}}
|
}}
|
||||||
schema={settingsSchema}
|
schema={settingsSchema}
|
||||||
onSubmit={async ({ tipDefault, nostrPubkey, nostrRelays, ...values }) => {
|
onSubmit={async ({ tipDefault, nostrPubkey, nostrRelays, ...values }) => {
|
||||||
|
@ -221,6 +222,11 @@ export default function Settings ({ ssrData }) {
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={<>click to load external images</>}
|
label={<>click to load external images</>}
|
||||||
name='clickToLoadImg'
|
name='clickToLoadImg'
|
||||||
|
groupClassName='mb-0'
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label={<>hide my bookmarks from other stackers</>}
|
||||||
|
name='hideBookmarks'
|
||||||
/>
|
/>
|
||||||
<div className='form-label'>content</div>
|
<div className='form-label'>content</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" ADD COLUMN "hideBookmarks" BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
|
|
@ -84,6 +84,7 @@ model User {
|
||||||
referrees User[] @relation("referrals")
|
referrees User[] @relation("referrals")
|
||||||
Account Account[]
|
Account Account[]
|
||||||
Session Session[]
|
Session Session[]
|
||||||
|
hideBookmarks Boolean @default(false)
|
||||||
|
|
||||||
@@index([createdAt], map: "users.created_at_index")
|
@@index([createdAt], map: "users.created_at_index")
|
||||||
@@index([inviteId], map: "users.inviteId_index")
|
@@index([inviteId], map: "users.inviteId_index")
|
||||||
|
|
Loading…
Reference in New Issue