diff --git a/packages/cms/src/access/require-one-of.ts b/packages/cms/src/access/require-one-of.ts index 54d004c5..c08f7cd6 100644 --- a/packages/cms/src/access/require-one-of.ts +++ b/packages/cms/src/access/require-one-of.ts @@ -1,10 +1,12 @@ import {type GeneratedTypes} from 'payload'; -import {type Access} from 'payload/config'; +import {type FieldAccess, type TypeWithID} from 'payload/types'; import {type SessionUser} from '../collections/users'; type Role = GeneratedTypes['collections']['users']['roles'][number]; -export function requireOneOf(...args: Role[]): Access { +export function requireOneOf( + ...args: Role[] +): FieldAccess { return ({req}) => { const user: SessionUser = req.user; diff --git a/packages/cms/src/collections/groupchats.ts b/packages/cms/src/collections/groupchats.ts index c6555c4b..f9e327a9 100644 --- a/packages/cms/src/collections/groupchats.ts +++ b/packages/cms/src/collections/groupchats.ts @@ -71,6 +71,19 @@ export const Groupchats: CollectionConfig = { {label: 'WhatsApp', value: 'whatsapp'}, ], }, + { + name: 'showUnauthenticated', + type: 'checkbox', + label: 'Show to unauthenticated users?', + defaultValue: false, + admin: { + description: + 'This potentially allows bots to scrape this groupchat from the page. If you have measures to prevent botting, you can select this for more visibility.', + // Facebook groups / Instagram profiles are always shown, so we just hide this checkbox in that case + condition: (data) => + data.platform && !['facebook', 'instagram'].includes(data.platform), + }, + }, {name: 'description', type: 'text'}, { name: 'url', @@ -92,6 +105,9 @@ export const Groupchats: CollectionConfig = { ], }, validate: (url, {data}) => { + // Validate only server-side to allow beforeValidate to run properly before this. + if (typeof window !== 'undefined') return true; + if (!url) return 'This field is required.'; const urlMatcher = platformUrlMatchers[data.platform]; @@ -116,8 +132,10 @@ export const Groupchats: CollectionConfig = { type: 'number', min: 0, max: 100, - required: true, defaultValue: 0, + access: { + read: requireOneOf('groupchats-admin'), + }, admin: { description: 'This value may be used to push results. A value of 0 means no promotion. Any value between 1 and 100 may be used to order promoted groupchats.', @@ -208,6 +226,8 @@ export const Groupchats: CollectionConfig = { const typesenseDoc = { ...sanitized, id: doc.id.toString(), + promoted: doc.promoted ?? 0, + showUnauthenticated: doc.showUnauthenticated ?? false, keywords: keywords.docs.map((k) => k.value), }; await typesenseClient diff --git a/packages/cms/src/cron-jobs/groupchat-sync.ts b/packages/cms/src/cron-jobs/groupchat-sync.ts index 9242dbab..986f7609 100644 --- a/packages/cms/src/cron-jobs/groupchat-sync.ts +++ b/packages/cms/src/cron-jobs/groupchat-sync.ts @@ -39,6 +39,10 @@ const schema: CollectionCreateSchema = { type: 'string', facet: true, }, + { + name: 'showUnauthenticated', + type: 'bool', + }, { name: 'promoted', type: 'int32', @@ -73,9 +77,10 @@ const syncGroupchatsToTypesense = async () => { }); const typesenseChats = groupchats.map( - ({id, createdAt, updatedAt, keywords, ...rest}) => ({ + ({id, createdAt, updatedAt, keywords, showUnauthenticated, ...rest}) => ({ ...rest, id: id.toString(), + showUnauthenticated: showUnauthenticated ?? false, keywords: keywords?.map((keyword) => typeof keyword === 'object' ? keyword.value : undefined, diff --git a/packages/cms/src/graphql/queries/groupchat-search-token.ts b/packages/cms/src/graphql/queries/groupchat-search-token.ts index 0d20beda..81b95983 100644 --- a/packages/cms/src/graphql/queries/groupchat-search-token.ts +++ b/packages/cms/src/graphql/queries/groupchat-search-token.ts @@ -15,13 +15,15 @@ export const groupchatSearchTokenQuery: QueryFactory = ( resolve: async (_: unknown, __, context) => { const {isLoggedIn} = await getAuthStateFromRequest(context.req); - const accessiblePlatforms: GroupchatPlatform[] = ['facebook']; + const accessiblePlatforms: GroupchatPlatform[] = ['facebook', 'instagram']; if (isLoggedIn) { accessiblePlatforms.push('discord', 'signal', 'telegram', 'whatsapp'); } - const filterBy = `platform:=[${accessiblePlatforms.join(',')}]`; + const filterBy = `platform:=[${accessiblePlatforms.join( + ',', + )}] || showUnauthenticated: true`; return typesenseClient .keys() diff --git a/packages/cms/src/payload-types.ts b/packages/cms/src/payload-types.ts index f52898ff..16774592 100644 --- a/packages/cms/src/payload-types.ts +++ b/packages/cms/src/payload-types.ts @@ -39,6 +39,7 @@ export interface Groupchat { description?: string | null; url: string; keywords?: (number | GroupchatKeyword)[] | null; + showUnauthenticated?: boolean | null; promoted: number; owners?: (string | User)[] | null; updatedAt: string; diff --git a/packages/scripts/src/scrape-groupchats/groupchat.ts b/packages/scripts/src/scrape-groupchats/groupchat.ts index 6c018015..4587459e 100644 --- a/packages/scripts/src/scrape-groupchats/groupchat.ts +++ b/packages/scripts/src/scrape-groupchats/groupchat.ts @@ -1,6 +1,7 @@ export type Groupchat = { name: string; url: string; + showUnauthenticated?: boolean; }; export type ProcessedGroupchat = Groupchat & {platform: string}; diff --git a/packages/scripts/src/scrape-groupchats/sources/wixsite.ts b/packages/scripts/src/scrape-groupchats/sources/wixsite.ts index 146cd997..497dc950 100644 --- a/packages/scripts/src/scrape-groupchats/sources/wixsite.ts +++ b/packages/scripts/src/scrape-groupchats/sources/wixsite.ts @@ -33,6 +33,7 @@ const scrapeFromWix = async (url: string): Promise => { return { url, name: a.innerText, + showUnauthenticated: true, }; }); }; diff --git a/packages/web/src/app/groupchats/components/group-chat-search.tsx b/packages/web/src/app/groupchats/components/group-chat-search.tsx index fb4dc740..747de511 100644 --- a/packages/web/src/app/groupchats/components/group-chat-search.tsx +++ b/packages/web/src/app/groupchats/components/group-chat-search.tsx @@ -34,22 +34,14 @@ export const GroupChatSearch: FC<{isLoggedIn: boolean}> = ({isLoggedIn}) => {

Not seeing what you are looking for?

- Ask the group's admin to shoot me a message{' '} - - over on Discord - {' '} - to get the group on here! + Ask the group's admin to head over{' '} + here!

{!isLoggedIn && (

- Only Facebook groups and Instagram pages are available without{' '} + Only specific chats are available without{' '} navigateToLogin()}