diff --git a/client/coral-framework/lib/action.js b/client/coral-framework/lib/action.js
new file mode 100644
index 000000000..6f22d01e3
--- /dev/null
+++ b/client/coral-framework/lib/action.js
@@ -0,0 +1,21 @@
+export default class Action {
+ listeners = [];
+
+ listen = cb => {
+ this.listeners.push(cb);
+ };
+
+ unlisten = cb => {
+ this.listeners = this.listeners.filter(i => i !== cb);
+ };
+
+ event = { listen: this.listen, unlisten: this.unlisten };
+
+ call(...args) {
+ this.listeners.forEach(cb => cb(...args));
+ }
+
+ asEvent() {
+ return this.event;
+ }
+}
diff --git a/plugin-api/client/lib/index.js b/plugin-api/client/lib/index.js
new file mode 100644
index 000000000..20e084806
--- /dev/null
+++ b/plugin-api/client/lib/index.js
@@ -0,0 +1 @@
+export { default as Action } from 'coral-framework/lib/action';
diff --git a/plugins/talk-plugin-notifications-category-reply/client/.eslintrc.json b/plugins/talk-plugin-notifications-category-reply/client/.eslintrc.json
new file mode 100644
index 000000000..c8a6db18a
--- /dev/null
+++ b/plugins/talk-plugin-notifications-category-reply/client/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@coralproject/eslint-config-talk/client"
+}
diff --git a/plugins/talk-plugin-notifications-category-reply/client/components/Toggle.css b/plugins/talk-plugin-notifications-category-reply/client/components/Toggle.css
new file mode 100644
index 000000000..b61b2fe4d
--- /dev/null
+++ b/plugins/talk-plugin-notifications-category-reply/client/components/Toggle.css
@@ -0,0 +1,3 @@
+.innerSettings {
+ padding-left: 24px;
+}
diff --git a/plugins/talk-plugin-notifications-category-reply/client/components/Toggle.js b/plugins/talk-plugin-notifications-category-reply/client/components/Toggle.js
new file mode 100644
index 000000000..a9a0ff519
--- /dev/null
+++ b/plugins/talk-plugin-notifications-category-reply/client/components/Toggle.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { t } from 'plugin-api/beta/client/services';
+import { Checkbox } from 'plugin-api/beta/client/components/ui';
+import styles from './Toggle.css';
+
+class Toggle extends React.Component {
+ render() {
+ const { checked } = this.props;
+ return (
+
+
+ {t('talk-plugin-notifications-category-reply.toggle_description')}
+
+
+
+ );
+ }
+}
+
+Toggle.propTypes = {
+ checked: PropTypes.bool,
+};
+
+export default Toggle;
diff --git a/plugins/talk-plugin-notifications-category-reply/client/containers/Toggle.js b/plugins/talk-plugin-notifications-category-reply/client/containers/Toggle.js
new file mode 100644
index 000000000..2d275e452
--- /dev/null
+++ b/plugins/talk-plugin-notifications-category-reply/client/containers/Toggle.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { compose, gql } from 'react-apollo';
+import Toggle from '../components/Toggle';
+import { withFragments } from 'plugin-api/beta/client/hocs';
+
+class ToggleContainer extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+ToggleContainer.propTypes = {
+ data: PropTypes.object,
+ root: PropTypes.object,
+};
+
+const enhance = compose(
+ withFragments({
+ root: gql`
+ fragment TalkNotificationsCategoryReply_Toggle_root on RootQuery {
+ me {
+ notificationSettings {
+ onReply
+ }
+ }
+ }
+ `,
+ })
+);
+
+export default enhance(ToggleContainer);
diff --git a/plugins/talk-plugin-notifications-category-reply/client/index.js b/plugins/talk-plugin-notifications-category-reply/client/index.js
new file mode 100644
index 000000000..38a37e6ef
--- /dev/null
+++ b/plugins/talk-plugin-notifications-category-reply/client/index.js
@@ -0,0 +1,9 @@
+import Toggle from './containers/Toggle';
+import translations from './translations.yml';
+
+export default {
+ slots: {
+ notificationSettings: [Toggle],
+ },
+ translations,
+};
diff --git a/plugins/talk-plugin-notifications-category-reply/client/translations.yml b/plugins/talk-plugin-notifications-category-reply/client/translations.yml
new file mode 100644
index 000000000..703891de5
--- /dev/null
+++ b/plugins/talk-plugin-notifications-category-reply/client/translations.yml
@@ -0,0 +1,3 @@
+en:
+ talk-plugin-notifications-category-reply:
+ toggle_description: My comment receives a reply
diff --git a/plugins/talk-plugin-notifications/client/.eslintrc.json b/plugins/talk-plugin-notifications/client/.eslintrc.json
new file mode 100644
index 000000000..c8a6db18a
--- /dev/null
+++ b/plugins/talk-plugin-notifications/client/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@coralproject/eslint-config-talk/client"
+}
diff --git a/plugins/talk-plugin-notifications/client/components/Settings.css b/plugins/talk-plugin-notifications/client/components/Settings.css
new file mode 100644
index 000000000..2980501cc
--- /dev/null
+++ b/plugins/talk-plugin-notifications/client/components/Settings.css
@@ -0,0 +1,8 @@
+.innerSettings {
+ padding-left: 12px;
+}
+
+.subtitle {
+ margin: 0;
+ margin-bottom: 8px;
+}
diff --git a/plugins/talk-plugin-notifications/client/components/Settings.js b/plugins/talk-plugin-notifications/client/components/Settings.js
new file mode 100644
index 000000000..b81b65373
--- /dev/null
+++ b/plugins/talk-plugin-notifications/client/components/Settings.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { IfSlotIsNotEmpty } from 'plugin-api/beta/client/components';
+import { Slot } from 'plugin-api/beta/client/components';
+import { t } from 'plugin-api/beta/client/services';
+import styles from './Settings.css';
+
+class Settings extends React.Component {
+ render() {
+ const { root } = this.props;
+ return (
+
+
+
{t('talk-plugin-notifications.settings_title')}
+
+ {t('talk-plugin-notifications.settings_subtitle')}
+
+
+
+
+
+
+ );
+ }
+}
+
+Settings.propTypes = {
+ root: PropTypes.object,
+};
+
+export default Settings;
diff --git a/plugins/talk-plugin-notifications/client/containers/Settings.js b/plugins/talk-plugin-notifications/client/containers/Settings.js
new file mode 100644
index 000000000..9eb16ceee
--- /dev/null
+++ b/plugins/talk-plugin-notifications/client/containers/Settings.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { compose, gql } from 'react-apollo';
+import Settings from '../components/Settings';
+import { withFragments } from 'plugin-api/beta/client/hocs';
+import { getSlotFragmentSpreads } from 'plugin-api/beta/client/utils';
+
+const slots = ['notificationSettings'];
+
+class SettingsContainer extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+SettingsContainer.propTypes = {
+ data: PropTypes.object,
+ root: PropTypes.object,
+};
+
+const enhance = compose(
+ withFragments({
+ root: gql`
+ fragment TalkNotifications_Settings_root on RootQuery {
+ __typename
+ ${getSlotFragmentSpreads(slots, 'root')}
+ }
+ `,
+ })
+);
+
+export default enhance(SettingsContainer);
diff --git a/plugins/talk-plugin-notifications/client/index.js b/plugins/talk-plugin-notifications/client/index.js
new file mode 100644
index 000000000..69b29888c
--- /dev/null
+++ b/plugins/talk-plugin-notifications/client/index.js
@@ -0,0 +1,9 @@
+import Settings from './containers/Settings';
+import translations from './translations.yml';
+
+export default {
+ slots: {
+ profileSettings: [Settings],
+ },
+ translations,
+};
diff --git a/plugins/talk-plugin-notifications/client/translations.yml b/plugins/talk-plugin-notifications/client/translations.yml
new file mode 100644
index 000000000..2054f8ac1
--- /dev/null
+++ b/plugins/talk-plugin-notifications/client/translations.yml
@@ -0,0 +1,4 @@
+en:
+ talk-plugin-notifications:
+ settings_title: Notifications
+ settings_subtitle: Receive notifications when