feat: personal setting notification change

This commit is contained in:
shuai 2023-08-21 15:32:57 +08:00
parent 41f7b1b9cd
commit 80df5193c5
8 changed files with 125 additions and 41 deletions

View File

@ -962,9 +962,16 @@ ui:
placeholder: "City, Country" placeholder: "City, Country"
notification: notification:
heading: Notifications heading: Notifications
email: email: Email
label: Email Notifications inbox:
radio: "Answers to your questions, comments, and more" label: Inbox notifications
description: Answers to your questions, comments, invites, and more.
all_new_question:
label: All new questions
description: Get notified of all new questions. Up to 50 questions per week.
all_new_question_for_following_tags:
label: All new questions for following tags
description: Get notified of new questions for following tags.
account: account:
heading: Account heading: Account
change_email_btn: Change email change_email_btn: Change email

View File

@ -57,7 +57,7 @@ export interface TagInfo extends TagBase {
main_tag_slug_name?: string; main_tag_slug_name?: string;
excerpt?; excerpt?;
} }
export interface QuestionParams extends ImgCodeReq{ export interface QuestionParams extends ImgCodeReq {
title: string; title: string;
url_title?: string; url_title?: string;
content: string; content: string;
@ -589,3 +589,13 @@ export interface UserOauthConnectorItem {
binding: boolean; binding: boolean;
external_id: string; external_id: string;
} }
export interface NotificationConfigItem {
enable: boolean;
key: string;
}
export interface NotificationConfig {
all_new_question: NotificationConfigItem[];
all_new_question_for_following_tags: NotificationConfigItem[];
inbox: NotificationConfigItem[];
}

View File

@ -27,12 +27,13 @@ const Index: FC<Props> = ({
index: number, index: number,
) => { ) => {
const { name, checked } = evt.currentTarget; const { name, checked } = evt.currentTarget;
const freshVal = checked ? enumValues?.[index] : ''; enumValues[index] = checked;
const state = { const state = {
...formData, ...formData,
[name]: { [name]: {
...formData[name], ...formData[name],
value: freshVal, value: enumValues,
isInvalid: false, isInvalid: false,
}, },
}; };
@ -51,7 +52,7 @@ const Index: FC<Props> = ({
name={fieldName} name={fieldName}
id={`form-${String(item)}`} id={`form-${String(item)}`}
label={enumNames?.[index]} label={enumNames?.[index]}
checked={(fieldObject?.value || '') === item} checked={fieldObject?.value?.[index] || false}
feedback={fieldObject?.errorMsg} feedback={fieldObject?.errorMsg}
feedbackType="invalid" feedbackType="invalid"
isInvalid={fieldObject?.isInvalid} isInvalid={fieldObject?.isInvalid}

View File

@ -386,8 +386,13 @@ export const initFormData = (schema: JSONSchema): Type.FormDataType => {
const props: JSONSchema['properties'] = schema?.properties || {}; const props: JSONSchema['properties'] = schema?.properties || {};
Object.keys(props).forEach((key) => { Object.keys(props).forEach((key) => {
const prop = props[key]; const prop = props[key];
const defaultVal = prop?.default; let defaultVal: any = '';
if (Array.isArray(prop.default) && prop.enum && prop.enum.length > 0) {
// for checkbox default values
defaultVal = prop.enum;
} else {
defaultVal = prop?.default;
}
formData[key] = { formData[key] = {
value: defaultVal, value: defaultVal,
isInvalid: false, isInvalid: false,

View File

@ -30,7 +30,7 @@ export interface JSONSchema {
description?: string; description?: string;
enum?: Array<string | boolean | number>; enum?: Array<string | boolean | number>;
enumNames?: string[]; enumNames?: string[];
default?: string | boolean | number; default?: string | boolean | number | any[];
}; };
}; };
} }

View File

@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { usePageTags } from '@/hooks'; import { usePageTags } from '@/hooks';
import * as Type from '@/common/interface'; import * as Type from '@/common/interface';
import { FollowingTags, CustomSidebar } from '@/components'; import { FollowingTags, CustomSidebar, Icon } from '@/components';
import { import {
useTagInfo, useTagInfo,
useFollow, useFollow,
@ -141,9 +141,16 @@ const Index: FC = () => {
<div className="box-ft"> <div className="box-ft">
{tagInfo.is_follower ? ( {tagInfo.is_follower ? (
<Button variant="primary" onClick={() => toggleFollow()}> <div>
{t('button_following')} <Button variant="primary" onClick={() => toggleFollow()}>
</Button> {t('button_following')}
</Button>
<Link
className="btn btn-outline-secondary ms-2"
to="/users/settings/notify">
<Icon name="bell-fill" />
</Link>
</div>
) : ( ) : (
<Button <Button
variant="outline-primary" variant="outline-primary"

View File

@ -1,9 +1,9 @@
import React, { useState, FormEvent, useEffect } from 'react'; import React, { useState, FormEvent, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import type { FormDataType } from '@/common/interface'; import type { FormDataType, NotificationConfig } from '@/common/interface';
import { useToast } from '@/hooks'; import { useToast } from '@/hooks';
import { setNotice, getLoggedUserInfo } from '@/services'; import { useGetNotificationConfig, putNotificationConfig } from '@/services';
import { SchemaForm, JSONSchema, UISchema, initFormData } from '@/components'; import { SchemaForm, JSONSchema, UISchema, initFormData } from '@/components';
const Index = () => { const Index = () => {
@ -11,46 +11,90 @@ const Index = () => {
const { t } = useTranslation('translation', { const { t } = useTranslation('translation', {
keyPrefix: 'settings.notification', keyPrefix: 'settings.notification',
}); });
const { data: configData } = useGetNotificationConfig();
const schema: JSONSchema = { const schema: JSONSchema = {
title: t('heading'), title: t('heading'),
properties: { properties: {
notice_switch: { inbox: {
type: 'boolean', type: 'boolean',
title: t('email.label'), title: t('inbox.label'),
default: false, description: t('inbox.description'),
enum: configData?.inbox?.map((v) => v.enable),
default: configData?.inbox?.map((v) => v.enable),
enumNames: configData?.inbox?.map((v) => t(v.key)),
},
all_new_question: {
type: 'boolean',
title: t('all_new_question.label'),
description: t('all_new_question.description'),
enum: configData?.all_new_question?.map((v) => v.enable),
default: configData?.all_new_question?.map((v) => v.enable),
enumNames: configData?.all_new_question?.map((v) => t(v.key)),
},
all_new_question_for_following_tags: {
type: 'boolean',
title: t('all_new_question_for_following_tags.label'),
description: t('all_new_question_for_following_tags.description'),
enum: configData?.all_new_question_for_following_tags?.map(
(v) => v.enable,
),
default: configData?.all_new_question_for_following_tags?.map(
(v) => v.enable,
),
enumNames: configData?.all_new_question_for_following_tags?.map((v) =>
t(v.key),
),
}, },
}, },
}; };
const uiSchema: UISchema = { const uiSchema: UISchema = {
notice_switch: { inbox: {
'ui:widget': 'switch', 'ui:widget': 'checkbox',
'ui:options': { 'ui:options': {
label: t('email.radio'), label: t('email'),
},
},
all_new_question: {
'ui:widget': 'checkbox',
'ui:options': {
label: t('email'),
},
},
all_new_question_for_following_tags: {
'ui:widget': 'checkbox',
'ui:options': {
label: t('email'),
text: t('all_new_question_for_following_tags.description'),
}, },
}, },
}; };
const [formData, setFormData] = useState<FormDataType>(initFormData(schema)); const [formData, setFormData] = useState<FormDataType>(initFormData(schema));
const getProfile = () => { useEffect(() => {
getLoggedUserInfo().then((res) => { setFormData(initFormData(schema));
if (res) { }, [configData]);
setFormData({
notice_switch: {
value: res.notice_status === 1,
isInvalid: false,
errorMsg: '',
},
});
}
});
};
const handleSubmit = (event: FormEvent) => { const handleSubmit = (event: FormEvent) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
setNotice({ const params = {
notice_switch: formData.notice_switch.value, inbox: configData?.inbox.map((v, index) => {
}).then(() => { return { enable: formData.inbox.value[index], key: v.key };
}),
all_new_question: configData?.all_new_question.map((v, index) => {
return { enable: formData.all_new_question.value[index], key: v.key };
}),
all_new_question_for_following_tags:
configData?.all_new_question_for_following_tags.map((v, index) => {
return {
enable: formData.all_new_question_for_following_tags.value[index],
key: v.key,
};
}),
} as NotificationConfig;
putNotificationConfig(params).then(() => {
toast.onShow({ toast.onShow({
msg: t('update', { keyPrefix: 'toast' }), msg: t('update', { keyPrefix: 'toast' }),
variant: 'success', variant: 'success',
@ -58,9 +102,6 @@ const Index = () => {
}); });
}; };
useEffect(() => {
getProfile();
}, []);
const handleChange = (ud) => { const handleChange = (ud) => {
setFormData(ud); setFormData(ud);
}; };

View File

@ -1,3 +1,5 @@
import useSWR from 'swr';
import request from '@/utils/request'; import request from '@/utils/request';
import type * as Type from '@/common/interface'; import type * as Type from '@/common/interface';
@ -14,3 +16,14 @@ export const updateUserInterface = (lang: string) => {
language: lang, language: lang,
}); });
}; };
export const useGetNotificationConfig = () => {
return useSWR<Type.NotificationConfig>(
'/answer/api/v1/user/notification/config',
request.instance.get,
);
};
export const putNotificationConfig = (data: Type.NotificationConfig) => {
return request.put('/answer/api/v1/user/notification/config', data);
};