feat: show max chars on main global edit

This commit is contained in:
Nevo David 2025-08-08 02:51:37 +07:00
parent aa12f100c8
commit 620439d175
4 changed files with 70 additions and 8 deletions

View File

@ -10,6 +10,7 @@ import React, {
ClipboardEvent,
forwardRef,
useImperativeHandle,
Fragment,
} from 'react';
import clsx from 'clsx';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
@ -20,7 +21,10 @@ import { BoldText } from '@gitroom/frontend/components/new-launch/bold.text';
import { UText } from '@gitroom/frontend/components/new-launch/u.text';
import { SignatureBox } from '@gitroom/frontend/components/signature';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store';
import {
SelectedIntegrations,
useLaunchStore,
} from '@gitroom/frontend/components/new-launch/store';
import { useShallow } from 'zustand/react/shallow';
import { AddPostButton } from '@gitroom/frontend/components/new-launch/add.post.button';
import { MultiMediaComponent } from '@gitroom/frontend/components/media/media.component';
@ -54,6 +58,7 @@ import Mention from '@tiptap/extension-mention';
import { suggestion } from '@gitroom/frontend/components/new-launch/mention.component';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { AComponent } from '@gitroom/frontend/components/new-launch/a.component';
import { capitalize } from 'lodash';
const InterceptBoldShortcut = Extension.create({
name: 'preventBoldWithUnderline',
@ -114,6 +119,8 @@ export const EditorWrapper: FC<{
editor,
loadedState,
setLoadedState,
selectedIntegration,
chars,
} = useLaunchStore(
useShallow((state) => ({
internal: state.internal.find((p) => p.integration.id === state.current),
@ -142,6 +149,8 @@ export const EditorWrapper: FC<{
editor: state.editor,
loadedState: state.loaded,
setLoadedState: state.setLoaded,
selectedIntegration: state.selectedIntegrations,
chars: state.chars,
}))
);
@ -357,6 +366,8 @@ export const EditorWrapper: FC<{
totalChars={totalChars}
appendImages={appendImages(index)}
dummy={dummy}
selectedIntegration={selectedIntegration}
chars={chars}
/>
</div>
<div className="flex flex-col items-center gap-[10px]">
@ -436,7 +447,9 @@ export const Editor: FC<{
validateChars?: boolean;
identifier?: string;
totalChars?: number;
selectedIntegration: SelectedIntegrations[];
dummy: boolean;
chars: Record<string, number>;
}> = (props) => {
const {
editorType = 'normal',
@ -444,11 +457,12 @@ export const Editor: FC<{
pictures,
setImages,
num,
autoComplete,
validateChars,
identifier,
appendImages,
selectedIntegration,
dummy,
chars,
} = props;
const user = useUser();
const [id] = useState(makeId(10));
@ -637,7 +651,7 @@ export const Editor: FC<{
</div>
</div>
<div className="absolute bottom-10px end-[25px]">
{(props?.totalChars || 0) > 0 && (
{(props?.totalChars || 0) > 0 ? (
<div
className={clsx(
'text-end text-sm mt-1',
@ -646,6 +660,23 @@ export const Editor: FC<{
>
{valueWithoutHtml.length}/{props.totalChars}
</div>
) : (
<div
className={clsx(
'text-end text-sm mt-1 grid grid-cols-[max-content_max-content] gap-x-[5px]'
)}
>
{selectedIntegration?.map((p) => (
<Fragment key={p.integration.id}>
<div className={valueWithoutHtml.length > chars?.[p.integration.id] && '!text-red-500'}>
{p.integration.name} ({capitalize(p.integration.identifier)}):
</div>
<div className={valueWithoutHtml.length > chars?.[p.integration.id] && '!text-red-500'}>
{valueWithoutHtml.length}/{chars?.[p.integration.id]}
</div>
</Fragment>
))}
</div>
)}
</div>
</div>

View File

@ -142,7 +142,6 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
(type: 'draft' | 'now' | 'schedule') => async () => {
setLoading(true);
const checkAllValid = await ref.current.checkAllValid();
console.log(checkAllValid);
if (type !== 'draft') {
const notEnoughChars = checkAllValid.filter((p: any) => {
return p.values.some((a: any) => {

View File

@ -89,6 +89,7 @@ export const withProvider = function <T extends object>(params: {
setPostComment,
setEditor,
dummy,
setChars,
} = useLaunchStore(
useShallow((state) => ({
date: state.date,
@ -106,6 +107,7 @@ export const withProvider = function <T extends object>(params: {
setTotalChars: state.setTotalChars,
setPostComment: state.setPostComment,
setEditor: state.setEditor,
setChars: state.setChars,
selectedIntegration: state.selectedIntegrations.find(
(p) => p.integration.id === props.id
),
@ -117,6 +119,17 @@ export const withProvider = function <T extends object>(params: {
return;
}
setChars(
props.id,
typeof maximumCharacters === 'number'
? maximumCharacters
: maximumCharacters(
JSON.parse(
selectedIntegration.integration.additionalSettings || '[]'
)
)
);
if (isGlobal) {
setPostComment(PostComment.ALL);
setTotalChars(0);
@ -245,7 +258,12 @@ export const withProvider = function <T extends object>(params: {
<div className="flex-1 flex">
<div
onClick={() => setTab(0)}
className={clsx("cursor-pointer rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px]", tab !== 0 && !!SettingsComponent ? '' : 'text-textItemFocused bg-boxFocused')}
className={clsx(
'cursor-pointer rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px]',
tab !== 0 && !!SettingsComponent
? ''
: 'text-textItemFocused bg-boxFocused'
)}
>
{t('preview', 'Preview')}
</div>
@ -255,12 +273,16 @@ export const withProvider = function <T extends object>(params: {
<div className="flex-1 flex">
<div
onClick={() => setTab(1)}
className={clsx("cursor-pointer rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px]", tab !== 1 ? '' : 'text-textItemFocused bg-boxFocused')}
className={clsx(
'cursor-pointer rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px]',
tab !== 1 ? '' : 'text-textItemFocused bg-boxFocused'
)}
>
{t('settings', 'Settings')} (
{capitalize(
selectedIntegration.integration.identifier.split('-')[0]
)})
)}
)
</div>
</div>
)}

View File

@ -19,7 +19,7 @@ interface Internal {
integrationValue: Values[];
}
interface SelectedIntegrations {
export interface SelectedIntegrations {
settings: any;
integration: Integrations;
ref?: RefObject<any>;
@ -123,6 +123,8 @@ interface StoreState {
setDummy: (dummy: boolean) => void;
setEditor: (editor: 'normal' | 'markdown' | 'html') => void;
setLoaded?: (loaded: boolean) => void;
setChars: (id: string, chars: number) => void;
chars: Record<string, number>;
}
const initialState = {
@ -143,6 +145,7 @@ const initialState = {
selectedIntegrations: [] as SelectedIntegrations[],
global: [] as Values[],
internal: [] as Internal[],
chars: {},
};
export const useLaunchStore = create<StoreState>()((set) => ({
@ -536,4 +539,11 @@ export const useLaunchStore = create<StoreState>()((set) => ({
set((state) => ({
loaded,
})),
setChars: (id: string, chars: number) =>
set((state) => ({
chars: {
...state.chars,
[id]: chars,
},
})),
}));