diff --git a/packages/shared/src/components/fields/form/common.tsx b/packages/shared/src/components/fields/form/common.tsx index bd3bde5fa5..e01ac0cbb8 100644 --- a/packages/shared/src/components/fields/form/common.tsx +++ b/packages/shared/src/components/fields/form/common.tsx @@ -1,6 +1,6 @@ export enum WriteFormTab { - Share = 'Share a link', NewPost = 'New post', + Share = 'Share a link', Poll = 'Poll', } diff --git a/packages/shared/src/components/post/freeform/write/WriteFreeFormSkeleton.tsx b/packages/shared/src/components/post/freeform/write/WriteFreeFormSkeleton.tsx index 1ea227b0e8..494830b09e 100644 --- a/packages/shared/src/components/post/freeform/write/WriteFreeFormSkeleton.tsx +++ b/packages/shared/src/components/post/freeform/write/WriteFreeFormSkeleton.tsx @@ -14,15 +14,13 @@ export function WriteFreeFormSkeleton({ return ( - - - - - - - - - + + + + + + + ); diff --git a/packages/shared/src/components/post/freeform/write/WritePostHeader.tsx b/packages/shared/src/components/post/freeform/write/WritePostHeader.tsx index 463d8f1166..5bee70a90a 100644 --- a/packages/shared/src/components/post/freeform/write/WritePostHeader.tsx +++ b/packages/shared/src/components/post/freeform/write/WritePostHeader.tsx @@ -14,7 +14,7 @@ export function WritePostHeader({ const { squad } = useWritePostContext(); return ( -
+

{isEdit ? 'Edit' : 'New'} post

{squad && squad.type === SourceType.Squad && ( diff --git a/packages/shared/src/components/profile/ProfileSettingsMenu.tsx b/packages/shared/src/components/profile/ProfileSettingsMenu.tsx index c290c4125a..57a43eb39d 100644 --- a/packages/shared/src/components/profile/ProfileSettingsMenu.tsx +++ b/packages/shared/src/components/profile/ProfileSettingsMenu.tsx @@ -31,6 +31,7 @@ import { JobIcon, TerminalIcon, TourIcon, + FeatherIcon, } from '../icons'; import { NavDrawer } from '../drawers/NavDrawer'; import { @@ -112,6 +113,11 @@ const useAccountPageItems = () => { icon: NewTabIcon, href: `${settingsUrl}/appearance`, }, + composition: { + title: 'Composition', + icon: FeatherIcon, + href: `${settingsUrl}/composition`, + }, notifications: { title: 'Notifications', icon: BellIcon, diff --git a/packages/shared/src/components/tabs/TabContainer.tsx b/packages/shared/src/components/tabs/TabContainer.tsx index 7c3cf9a9a0..3fad5ef2aa 100644 --- a/packages/shared/src/components/tabs/TabContainer.tsx +++ b/packages/shared/src/components/tabs/TabContainer.tsx @@ -50,6 +50,7 @@ export interface TabContainerProps { style?: CSSProperties; tabListProps?: Pick; tabTag?: AllowedTabTags; + extraHeaderContent?: ReactNode; } export function TabContainer({ @@ -65,6 +66,7 @@ export function TabContainer({ style, tabListProps = {}, tabTag, + extraHeaderContent, }: TabContainerProps): ReactElement { const router = useRouter(); const containerRef = useRef(null); @@ -168,6 +170,7 @@ export function TabContainer({ autoScrollActive={tabListProps?.autoScrollActive} tag={tabTag} /> + {extraHeaderContent}
{render} diff --git a/packages/shared/src/contexts/SettingsContext.tsx b/packages/shared/src/contexts/SettingsContext.tsx index 065effa84e..bf1e3f8b3a 100644 --- a/packages/shared/src/contexts/SettingsContext.tsx +++ b/packages/shared/src/contexts/SettingsContext.tsx @@ -17,6 +17,7 @@ import { CampaignCtaPlacement, UPDATE_USER_SETTINGS_MUTATION, } from '../graphql/settings'; +import { WriteFormTab } from '../components/fields/form/common'; import AuthContext from './AuthContext'; import { capitalize } from '../lib/strings'; import { storageWrapper } from '../lib/storageWrapper'; @@ -136,6 +137,7 @@ const defaultSettings: RemoteSettings = { sidebarResourcesExpanded: true, sidebarBookmarksExpanded: true, clickbaitShieldEnabled: true, + defaultWriteTab: WriteFormTab.NewPost, }, }; diff --git a/packages/shared/src/graphql/settings.ts b/packages/shared/src/graphql/settings.ts index df5c764025..03862d6b0a 100644 --- a/packages/shared/src/graphql/settings.ts +++ b/packages/shared/src/graphql/settings.ts @@ -1,5 +1,6 @@ import { gql } from 'graphql-request'; import type { SortCommentsBy } from './comments'; +import type { WriteFormTab } from '../components/fields/form/common'; export type Spaciness = 'eco' | 'roomy' | 'cozy'; export type RemoteTheme = 'darcula' | 'bright' | 'auto'; @@ -19,6 +20,7 @@ export type SettingsFlags = { timezoneMismatchIgnore?: string; prompt?: Record; lastPrompt?: string; + defaultWriteTab?: WriteFormTab; }; export enum SidebarSettingsFlags { diff --git a/packages/webapp/pages/settings/appearance.tsx b/packages/webapp/pages/settings/appearance.tsx index 3a0b6e2169..efc58501f7 100644 --- a/packages/webapp/pages/settings/appearance.tsx +++ b/packages/webapp/pages/settings/appearance.tsx @@ -10,6 +10,7 @@ import { TypographyColor, TypographyType, } from '@dailydotdev/shared/src/components/typography/Typography'; +import type { RadioItemProps } from '@dailydotdev/shared/src/components/fields/Radio'; import { Radio } from '@dailydotdev/shared/src/components/fields/Radio'; import { ToggleRadio } from '@dailydotdev/shared/src/components/fields/ToggleRadio'; import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; @@ -19,13 +20,14 @@ import { TargetType, } from '@dailydotdev/shared/src/lib/log'; import classNames from 'classnames'; +import { FlexCol } from '@dailydotdev/shared/src/components/utilities'; import { AccountPageContainer } from '../../components/layouts/SettingsLayout/AccountPageContainer'; import { getSettingsLayout } from '../../components/layouts/SettingsLayout'; import { defaultSeo } from '../../next-seo'; import { getTemplatedTitle } from '../../components/layouts/utils'; import { SettingsSwitch } from '../../components/layouts/SettingsLayout/common'; -const densities = [ +const densities: RadioItemProps[] = [ { label: 'Eco', value: 'eco' }, { label: 'Roomy', value: 'roomy' }, { label: 'Cozy', value: 'cozy' }, @@ -64,11 +66,11 @@ const AccountManageSubscriptionPage = (): ReactElement => { return ( -
+ {isLaptop && ( -
+ Layout @@ -86,10 +88,10 @@ const AccountManageSubscriptionPage = (): ReactElement => { offLabel="Cards" onLabel="List" /> -
+
)} -
+ Density @@ -119,9 +121,9 @@ const AccountManageSubscriptionPage = (): ReactElement => { }} reverse /> -
+ -
+ Preferences @@ -149,9 +151,9 @@ const AccountManageSubscriptionPage = (): ReactElement => { > Show companion widget on external sites -
+ -
+ Accessibility @@ -163,8 +165,8 @@ const AccountManageSubscriptionPage = (): ReactElement => { > Auto-hide notifications after a few seconds -
-
+ +
); }; diff --git a/packages/webapp/pages/settings/composition.tsx b/packages/webapp/pages/settings/composition.tsx new file mode 100644 index 0000000000..419fefa50d --- /dev/null +++ b/packages/webapp/pages/settings/composition.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import type { ReactElement } from 'react'; +import type { NextSeoProps } from 'next-seo'; + +import { useSettingsContext } from '@dailydotdev/shared/src/contexts/SettingsContext'; +import { + Typography, + TypographyType, +} from '@dailydotdev/shared/src/components/typography/Typography'; +import type { RadioItemProps } from '@dailydotdev/shared/src/components/fields/Radio'; +import { Radio } from '@dailydotdev/shared/src/components/fields/Radio'; + +import { WriteFormTab } from '@dailydotdev/shared/src/components/fields/form/common'; +import { FlexCol } from '@dailydotdev/shared/src/components/utilities'; +import { AccountPageContainer } from '../../components/layouts/SettingsLayout/AccountPageContainer'; +import { getSettingsLayout } from '../../components/layouts/SettingsLayout'; +import { defaultSeo } from '../../next-seo'; +import { getTemplatedTitle } from '../../components/layouts/utils'; + +const defaultWriteTabs: RadioItemProps[] = Object.keys(WriteFormTab).map( + (key) => ({ + label: WriteFormTab[key], + value: key, + }), +); + +const AccountManageSubscriptionPage = (): ReactElement => { + const { updateFlag, flags } = useSettingsContext(); + + return ( + +
+ + + Default post type + + + { + updateFlag('defaultWriteTab', value); + }} + className={{ + content: 'w-full justify-between !pr-0', + container: '!gap-0', + label: 'font-normal text-text-secondary typo-callout', + }} + reverse + /> + + + ); +}; + +const seo: NextSeoProps = { + ...defaultSeo, + title: getTemplatedTitle('Appearance'), +}; + +AccountManageSubscriptionPage.getLayout = getSettingsLayout; +AccountManageSubscriptionPage.layoutProps = { seo }; + +export default AccountManageSubscriptionPage; diff --git a/packages/webapp/pages/squads/create.tsx b/packages/webapp/pages/squads/create.tsx index 78c3a05574..a09b15795b 100644 --- a/packages/webapp/pages/squads/create.tsx +++ b/packages/webapp/pages/squads/create.tsx @@ -36,11 +36,19 @@ import { useQueryClient } from '@tanstack/react-query'; import CreatePoll from '@dailydotdev/shared/src/components/post/poll/CreatePoll'; import { Pill, PillSize } from '@dailydotdev/shared/src/components/Pill'; import { useMultipleSourcePost } from '@dailydotdev/shared/src/features/squads/hooks/useMultipleSourcePost'; -import { webappUrl } from '@dailydotdev/shared/src/lib/constants'; +import { settingsUrl, webappUrl } from '@dailydotdev/shared/src/lib/constants'; import type { WriteForm } from '@dailydotdev/shared/src/contexts'; -import { getLayout as getMainLayout } from '../../components/layouts/MainLayout'; -import { defaultOpenGraph, defaultSeo } from '../../next-seo'; +import { useSettingsContext } from '@dailydotdev/shared/src/contexts/SettingsContext'; + +import { + Button, + ButtonSize, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { SettingsIcon } from '@dailydotdev/shared/src/components/icons'; +import { LinkWithTooltip } from '@dailydotdev/shared/src/components/tooltips/LinkWithTooltip'; import { getTemplatedTitle } from '../../components/layouts/utils'; +import { defaultOpenGraph, defaultSeo } from '../../next-seo'; +import { getLayout as getMainLayout } from '../../components/layouts/MainLayout'; const seo: NextSeoProps = { title: getTemplatedTitle('Create post'), @@ -59,6 +67,10 @@ function CreatePost(): ReactElement { ); const { push, isReady: isRouteReady, query } = useRouter(); const { squads, user, isAuthReady, isFetched } = useAuthContext(); + const { + flags: { defaultWriteTab }, + loadedSettings, + } = useSettingsContext(); const [selectedSourceIds, setSelectedSourceIds] = useState([]); const activeSquads = useMemo(() => { const collator = new Intl.Collator('en'); @@ -191,6 +203,8 @@ function CreatePost(): ReactElement { setDisplay(WriteFormTab.Share); } else if (isInitialPoll) { setDisplay(WriteFormTab.Poll); + } else if (defaultWriteTab in WriteFormTab) { + setDisplay(WriteFormTab[defaultWriteTab]); } const preselectedSquad = @@ -220,6 +234,7 @@ function CreatePost(): ReactElement { activeSquads, selectedSourceIds.length, query, + defaultWriteTab, ]); useEffect(() => { @@ -228,7 +243,7 @@ function CreatePost(): ReactElement { } }, [display, hasCheckedPollTab, completeAction]); - if (!isFetched || !isAuthReady || !isRouteReady) { + if (!isFetched || !isAuthReady || !isRouteReady || !loadedSettings) { return ; } @@ -255,6 +270,28 @@ function CreatePost(): ReactElement { shouldMountInactive className={{ header: 'px-1' }} showHeader={isTablet} + extraHeaderContent={ + !isMobile && ( + + You can change the default post type settings +
+ ), + placement: 'left', + }} + href={`${settingsUrl}/composition`} + passHref + > +