Skip to content

Commit 80df519

Browse files
author
shuai
committed
feat: personal setting notification change
1 parent 41f7b1b commit 80df519

8 files changed

Lines changed: 125 additions & 41 deletions

File tree

i18n/en_US.yaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -962,9 +962,16 @@ ui:
962962
placeholder: "City, Country"
963963
notification:
964964
heading: Notifications
965-
email:
966-
label: Email Notifications
967-
radio: "Answers to your questions, comments, and more"
965+
email: Email
966+
inbox:
967+
label: Inbox notifications
968+
description: Answers to your questions, comments, invites, and more.
969+
all_new_question:
970+
label: All new questions
971+
description: Get notified of all new questions. Up to 50 questions per week.
972+
all_new_question_for_following_tags:
973+
label: All new questions for following tags
974+
description: Get notified of new questions for following tags.
968975
account:
969976
heading: Account
970977
change_email_btn: Change email

ui/src/common/interface.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export interface TagInfo extends TagBase {
5757
main_tag_slug_name?: string;
5858
excerpt?;
5959
}
60-
export interface QuestionParams extends ImgCodeReq{
60+
export interface QuestionParams extends ImgCodeReq {
6161
title: string;
6262
url_title?: string;
6363
content: string;
@@ -589,3 +589,13 @@ export interface UserOauthConnectorItem {
589589
binding: boolean;
590590
external_id: string;
591591
}
592+
593+
export interface NotificationConfigItem {
594+
enable: boolean;
595+
key: string;
596+
}
597+
export interface NotificationConfig {
598+
all_new_question: NotificationConfigItem[];
599+
all_new_question_for_following_tags: NotificationConfigItem[];
600+
inbox: NotificationConfigItem[];
601+
}

ui/src/components/SchemaForm/components/Check.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ const Index: FC<Props> = ({
2727
index: number,
2828
) => {
2929
const { name, checked } = evt.currentTarget;
30-
const freshVal = checked ? enumValues?.[index] : '';
30+
enumValues[index] = checked;
31+
3132
const state = {
3233
...formData,
3334
[name]: {
3435
...formData[name],
35-
value: freshVal,
36+
value: enumValues,
3637
isInvalid: false,
3738
},
3839
};
@@ -51,7 +52,7 @@ const Index: FC<Props> = ({
5152
name={fieldName}
5253
id={`form-${String(item)}`}
5354
label={enumNames?.[index]}
54-
checked={(fieldObject?.value || '') === item}
55+
checked={fieldObject?.value?.[index] || false}
5556
feedback={fieldObject?.errorMsg}
5657
feedbackType="invalid"
5758
isInvalid={fieldObject?.isInvalid}

ui/src/components/SchemaForm/index.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,8 +386,13 @@ export const initFormData = (schema: JSONSchema): Type.FormDataType => {
386386
const props: JSONSchema['properties'] = schema?.properties || {};
387387
Object.keys(props).forEach((key) => {
388388
const prop = props[key];
389-
const defaultVal = prop?.default;
390-
389+
let defaultVal: any = '';
390+
if (Array.isArray(prop.default) && prop.enum && prop.enum.length > 0) {
391+
// for checkbox default values
392+
defaultVal = prop.enum;
393+
} else {
394+
defaultVal = prop?.default;
395+
}
391396
formData[key] = {
392397
value: defaultVal,
393398
isInvalid: false,

ui/src/components/SchemaForm/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface JSONSchema {
3030
description?: string;
3131
enum?: Array<string | boolean | number>;
3232
enumNames?: string[];
33-
default?: string | boolean | number;
33+
default?: string | boolean | number | any[];
3434
};
3535
};
3636
}

ui/src/pages/Tags/Detail/index.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
1010

1111
import { usePageTags } from '@/hooks';
1212
import * as Type from '@/common/interface';
13-
import { FollowingTags, CustomSidebar } from '@/components';
13+
import { FollowingTags, CustomSidebar, Icon } from '@/components';
1414
import {
1515
useTagInfo,
1616
useFollow,
@@ -141,9 +141,16 @@ const Index: FC = () => {
141141

142142
<div className="box-ft">
143143
{tagInfo.is_follower ? (
144-
<Button variant="primary" onClick={() => toggleFollow()}>
145-
{t('button_following')}
146-
</Button>
144+
<div>
145+
<Button variant="primary" onClick={() => toggleFollow()}>
146+
{t('button_following')}
147+
</Button>
148+
<Link
149+
className="btn btn-outline-secondary ms-2"
150+
to="/users/settings/notify">
151+
<Icon name="bell-fill" />
152+
</Link>
153+
</div>
147154
) : (
148155
<Button
149156
variant="outline-primary"

ui/src/pages/Users/Settings/Notification/index.tsx

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,107 @@
11
import React, { useState, FormEvent, useEffect } from 'react';
22
import { useTranslation } from 'react-i18next';
33

4-
import type { FormDataType } from '@/common/interface';
4+
import type { FormDataType, NotificationConfig } from '@/common/interface';
55
import { useToast } from '@/hooks';
6-
import { setNotice, getLoggedUserInfo } from '@/services';
6+
import { useGetNotificationConfig, putNotificationConfig } from '@/services';
77
import { SchemaForm, JSONSchema, UISchema, initFormData } from '@/components';
88

99
const Index = () => {
1010
const toast = useToast();
1111
const { t } = useTranslation('translation', {
1212
keyPrefix: 'settings.notification',
1313
});
14+
const { data: configData } = useGetNotificationConfig();
15+
1416
const schema: JSONSchema = {
1517
title: t('heading'),
1618
properties: {
17-
notice_switch: {
19+
inbox: {
20+
type: 'boolean',
21+
title: t('inbox.label'),
22+
description: t('inbox.description'),
23+
enum: configData?.inbox?.map((v) => v.enable),
24+
default: configData?.inbox?.map((v) => v.enable),
25+
enumNames: configData?.inbox?.map((v) => t(v.key)),
26+
},
27+
all_new_question: {
28+
type: 'boolean',
29+
title: t('all_new_question.label'),
30+
description: t('all_new_question.description'),
31+
enum: configData?.all_new_question?.map((v) => v.enable),
32+
default: configData?.all_new_question?.map((v) => v.enable),
33+
enumNames: configData?.all_new_question?.map((v) => t(v.key)),
34+
},
35+
all_new_question_for_following_tags: {
1836
type: 'boolean',
19-
title: t('email.label'),
20-
default: false,
37+
title: t('all_new_question_for_following_tags.label'),
38+
description: t('all_new_question_for_following_tags.description'),
39+
enum: configData?.all_new_question_for_following_tags?.map(
40+
(v) => v.enable,
41+
),
42+
default: configData?.all_new_question_for_following_tags?.map(
43+
(v) => v.enable,
44+
),
45+
enumNames: configData?.all_new_question_for_following_tags?.map((v) =>
46+
t(v.key),
47+
),
2148
},
2249
},
2350
};
2451
const uiSchema: UISchema = {
25-
notice_switch: {
26-
'ui:widget': 'switch',
52+
inbox: {
53+
'ui:widget': 'checkbox',
54+
'ui:options': {
55+
label: t('email'),
56+
},
57+
},
58+
all_new_question: {
59+
'ui:widget': 'checkbox',
2760
'ui:options': {
28-
label: t('email.radio'),
61+
label: t('email'),
62+
},
63+
},
64+
all_new_question_for_following_tags: {
65+
'ui:widget': 'checkbox',
66+
'ui:options': {
67+
label: t('email'),
68+
text: t('all_new_question_for_following_tags.description'),
2969
},
3070
},
3171
};
3272
const [formData, setFormData] = useState<FormDataType>(initFormData(schema));
3373

34-
const getProfile = () => {
35-
getLoggedUserInfo().then((res) => {
36-
if (res) {
37-
setFormData({
38-
notice_switch: {
39-
value: res.notice_status === 1,
40-
isInvalid: false,
41-
errorMsg: '',
42-
},
43-
});
44-
}
45-
});
46-
};
74+
useEffect(() => {
75+
setFormData(initFormData(schema));
76+
}, [configData]);
4777

4878
const handleSubmit = (event: FormEvent) => {
4979
event.preventDefault();
5080
event.stopPropagation();
51-
setNotice({
52-
notice_switch: formData.notice_switch.value,
53-
}).then(() => {
81+
const params = {
82+
inbox: configData?.inbox.map((v, index) => {
83+
return { enable: formData.inbox.value[index], key: v.key };
84+
}),
85+
all_new_question: configData?.all_new_question.map((v, index) => {
86+
return { enable: formData.all_new_question.value[index], key: v.key };
87+
}),
88+
all_new_question_for_following_tags:
89+
configData?.all_new_question_for_following_tags.map((v, index) => {
90+
return {
91+
enable: formData.all_new_question_for_following_tags.value[index],
92+
key: v.key,
93+
};
94+
}),
95+
} as NotificationConfig;
96+
97+
putNotificationConfig(params).then(() => {
5498
toast.onShow({
5599
msg: t('update', { keyPrefix: 'toast' }),
56100
variant: 'success',
57101
});
58102
});
59103
};
60104

61-
useEffect(() => {
62-
getProfile();
63-
}, []);
64105
const handleChange = (ud) => {
65106
setFormData(ud);
66107
};

ui/src/services/client/settings.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import useSWR from 'swr';
2+
13
import request from '@/utils/request';
24
import type * as Type from '@/common/interface';
35

@@ -14,3 +16,14 @@ export const updateUserInterface = (lang: string) => {
1416
language: lang,
1517
});
1618
};
19+
20+
export const useGetNotificationConfig = () => {
21+
return useSWR<Type.NotificationConfig>(
22+
'/answer/api/v1/user/notification/config',
23+
request.instance.get,
24+
);
25+
};
26+
27+
export const putNotificationConfig = (data: Type.NotificationConfig) => {
28+
return request.put('/answer/api/v1/user/notification/config', data);
29+
};

0 commit comments

Comments
 (0)