-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TNO-1136: User can set toning #2226
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import { Form, Formik } from 'formik'; | ||
import React from 'react'; | ||
import { useApp } from 'store/hooks'; | ||
import { useApp, useLookup } from 'store/hooks'; | ||
import { useProfileStore } from 'store/slices'; | ||
import { | ||
Col, | ||
ContentTypeName, | ||
FormikSentiment, | ||
IContentModel, | ||
Loading, | ||
Show, | ||
|
@@ -12,6 +14,8 @@ import { | |
Wysiwyg, | ||
} from 'tno-core'; | ||
|
||
import { sentimentFormSchema } from '../../validation/SentimentFormSchema'; | ||
|
||
export interface IContentFormProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'content'> { | ||
/** The content being edited */ | ||
content?: IContentModel; | ||
|
@@ -43,20 +47,47 @@ export const ContentForm: React.FC<IContentFormProps> = ({ | |
}) => { | ||
const [{ userInfo }] = useApp(); | ||
const [{ impersonate }] = useProfileStore(); | ||
const [{ tonePools }] = useLookup(); | ||
|
||
if (!content) return null; | ||
|
||
const userId = impersonate?.id ?? userInfo?.id ?? 0; | ||
const isAV = content.contentType === ContentTypeName.AudioVideo; | ||
const versions = content.versions?.[userId] ?? { | ||
byline: content.byline, | ||
headline: content.headline, | ||
summary: '', | ||
body: isAV | ||
? content.isApproved && content.body | ||
? content.body | ||
: content.summary | ||
: content.body, | ||
|
||
const sentiment = | ||
content.versions?.[userId]?.tone !== undefined && content.versions?.[userId]?.tone != null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I can tell There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First time the tone can be null when the user did not save her/his tone and until tone is saved by the user, then we have a value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it can be |
||
? content.versions?.[userId]?.tone | ||
: content.tonePools.length | ||
? content.tonePools[0].value | ||
: undefined; | ||
|
||
const versions = { | ||
...content.versions?.[userId], | ||
byline: content.versions?.[userId]?.byline ?? content.byline, | ||
headline: content.versions?.[userId]?.headline ?? content.headline, | ||
summary: content.versions?.[userId]?.summary ?? '', | ||
body: | ||
content.versions?.[userId]?.body ?? | ||
(isAV ? (content.isApproved && content.body ? content.body : content.summary) : content.body), | ||
tonePools: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are you using a property |
||
sentiment !== undefined && sentiment !== null | ||
? [{ value: sentiment, id: tonePools[0].id }] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no reason to apply the wrong |
||
: [], | ||
}; | ||
|
||
const handleSentimentChange = (newTonePools: any) => { | ||
const validTonePools = Array.isArray(newTonePools) ? newTonePools : []; | ||
const updatedContent = { | ||
...content, | ||
versions: { | ||
...content.versions, | ||
[userId]: { | ||
...content.versions?.[userId], | ||
tone: validTonePools[0]?.value, | ||
}, | ||
}, | ||
}; | ||
onContentChange?.(updatedContent); | ||
}; | ||
|
||
return show === 'none' ? null : ( | ||
|
@@ -152,6 +183,19 @@ export const ContentForm: React.FC<IContentFormProps> = ({ | |
onContentChange?.(values); | ||
}} | ||
/> | ||
<Formik initialValues={versions} validationSchema={sentimentFormSchema} onSubmit={() => {}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't create a sub-form simply to work around the behavior of the FormikSentiment. Make the component smarter. |
||
{() => ( | ||
<Form> | ||
<FormikSentiment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is this component able to show the current value the user selected when the page is reloaded? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Through using handleSentimentChange callback which is executed when the user clicks on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That function saves the value. The component has no way to know the current value. If you refresh the page it wouldn't know how to get the value. |
||
name="tonePools" | ||
options={tonePools} | ||
coloredIcon={true} | ||
onSentimentChange={handleSentimentChange} | ||
required | ||
/> | ||
</Form> | ||
)} | ||
</Formik> | ||
</Show> | ||
</Col> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { array, number, object } from 'yup'; | ||
|
||
export const sentimentFormSchema = object().shape({ | ||
tonePools: array().of( | ||
object().shape({ | ||
value: number().required('Tone value is required'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe tone is required. Did Scott ask for this? |
||
}), | ||
), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,10 @@ public class ContentVersion | |
/// </summary> | ||
public bool IsPrivate { get; set; } = false; | ||
|
||
/// <summary> | ||
/// get/set - Tone for the content | ||
/// </summary> | ||
public int? Tone {get;set;} | ||
|
||
#endregion | ||
|
||
|
@@ -82,6 +86,7 @@ public ContentVersion(Content content, User owner) | |
this.Body = content.Body; | ||
this.SourceUrl = content.SourceUrl; | ||
this.IsPrivate = content.IsPrivate; | ||
this.Tone = content.TonePools.Count > 0 ? (int?)content.TonePools[0].Id : null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This incorrect, it should be the |
||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { getIn, useFormikContext } from 'formik'; | ||
import React from 'react'; | ||
import { FaFrown, FaMeh, FaSmile } from 'react-icons/fa'; | ||
|
||
import FaceFrownOpen from '../../../assets/face-frown-open.svg'; | ||
import FaceGrinWide from '../../../assets/face-grin-wide.svg'; | ||
|
@@ -17,10 +18,14 @@ export interface IFormikSentimentProps<T> { | |
label?: string; | ||
/** Sentiment options. */ | ||
options: ITonePoolModel[]; | ||
/** Sentiment icon colored */ | ||
coloredIcon?: boolean; | ||
/** The name of the default tone pool */ | ||
defaultTonePoolName?: string; | ||
/** The id of the default tone pool */ | ||
defaultTonePoolId?: number; | ||
/** Update upper form */ | ||
onSentimentChange?: (tonePools: any) => void; | ||
/** Whether this field is required. */ | ||
required?: boolean; | ||
} | ||
|
@@ -34,8 +39,10 @@ export const FormikSentiment = <T extends object>({ | |
name, | ||
label = 'Sentiment', | ||
options, | ||
coloredIcon = false, | ||
defaultTonePoolId, | ||
defaultTonePoolName = 'Default', | ||
onSentimentChange, | ||
...rest | ||
}: IFormikSentimentProps<T>) => { | ||
const { values, setFieldValue, touched, errors } = useFormikContext<T>(); | ||
|
@@ -44,8 +51,10 @@ export const FormikSentiment = <T extends object>({ | |
name, | ||
label, | ||
options, | ||
coloredIcon, | ||
defaultTonePoolId, | ||
defaultTonePoolName, | ||
onSentimentChange, | ||
...rest, | ||
}; | ||
const defaultTonePool = options.find( | ||
|
@@ -68,11 +77,23 @@ export const FormikSentiment = <T extends object>({ | |
|
||
const determineIndicator = (option: number) => { | ||
if (option === 5) { | ||
return <img alt={option.toString()} src={FaceGrinWide} />; | ||
return coloredIcon ? ( | ||
<FaSmile className="tone-icon" color="#59E9BE" /> | ||
) : ( | ||
<img alt={option.toString()} src={FaceGrinWide} /> | ||
); | ||
} else if (option === 0) { | ||
return <img alt={option.toString()} src={FaceMeh} />; | ||
return coloredIcon ? ( | ||
<FaMeh className="tone-icon" color="#F1C02D" /> | ||
) : ( | ||
<img alt={option.toString()} src={FaceMeh} /> | ||
); | ||
} else if (option === -5) { | ||
return <img alt={option.toString()} src={FaceFrownOpen} />; | ||
return coloredIcon ? ( | ||
<FaFrown className="tone-icon" color="#EB8585" /> | ||
) : ( | ||
<img alt={option.toString()} src={FaceFrownOpen} /> | ||
); | ||
} else { | ||
return <span className="blank"> </span>; | ||
} | ||
|
@@ -92,11 +113,11 @@ export const FormikSentiment = <T extends object>({ | |
key={option} | ||
onClick={() => { | ||
setActive(option); | ||
setFieldValue( | ||
name.toString(), | ||
!!defaultTonePool ? [{ ...defaultTonePool, value: option }] : [], | ||
true, | ||
); | ||
const updatedTonePools = !!defaultTonePool | ||
? [{ ...defaultTonePool, value: option }] | ||
: []; | ||
setFieldValue(name.toString(), updatedTonePools, true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is going to modify the the formik context field and also call the |
||
onSentimentChange?.(updatedTonePools); | ||
}} | ||
> | ||
{option} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell
tone
should never benull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First time the
tone
can be null when the user did not save her/his tone and untiltone
is saved by the user, then we have a value.