From ab11c912db1af9a5b714d012851391630701943c Mon Sep 17 00:00:00 2001
From: Ildar Kamalov <i.kamalov@adguard.com>
Date: Fri, 25 Jan 2019 15:18:05 +0300
Subject: [PATCH] Added topline component and fixed string interpolation

---
 client/src/__locales/en.json                  |  3 ++-
 client/src/actions/index.js                   |  9 +++++++-
 client/src/components/App/index.js            | 21 +++++++++++++------
 .../components/Settings/Encryption/Form.js    | 17 ++++++++++-----
 .../components/ui/{Update.css => Topline.css} |  2 +-
 client/src/components/ui/Topline.js           | 19 +++++++++++++++++
 client/src/components/ui/Update.js            | 19 -----------------
 client/src/containers/App.js                  |  4 ++--
 8 files changed, 59 insertions(+), 35 deletions(-)
 rename client/src/components/ui/{Update.css => Topline.css} (89%)
 create mode 100644 client/src/components/ui/Topline.js
 delete mode 100644 client/src/components/ui/Update.js

diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json
index 36bd3501..25053476 100644
--- a/client/src/__locales/en.json
+++ b/client/src/__locales/en.json
@@ -223,13 +223,14 @@
     "encryption_dot": "DNS-over-TLS port",
     "encryption_dot_desc": "If this port is configured, AdGuard Home will run a DNS-over-TLS server on this port.",
     "encryption_certificates": "Certificates",
-    "encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on letsencrypt.org or you can buy it from one of the trusted Certificate Authorities.",
+    "encryption_certificates_desc": "In order to use encryption, you need to provide a valid SSL certificates chain for your domain. You can get a free certificate on <0>{{link}}</0> or you can buy it from one of the trusted Certificate Authorities.",
     "encryption_certificates_input": "Copy/paste your PEM-encoded cerificates here.",
     "encryption_status": "Status",
     "encryption_certificates_for": "Certificates for {{domains}}",
     "encryption_expire": "Expire on {{date}}",
     "encryption_key": "Private key",
     "encryption_key_input": "Copy/paste your PEM-encoded private key for your cerficate here.",
+    "topline_expiring_certificate": "Your SSL certificate is about to expire. Update <0>Encryption settings</0>.",
     "form_error_port_range": "Enter port value in the range of 80-65535",
     "form_error_equal": "Shouldn't be equal",
     "form_error_password": "Password mismatched"
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index da33f0fe..30527245 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -659,6 +659,9 @@ export const getTlsStatus = () => async (dispatch) => {
     dispatch(getTlsStatusRequest());
     try {
         const status = await apiClient.getTlsStatus();
+        status.certificate_chain = decodeURIComponent(status.certificate_chain);
+        status.private_key = decodeURIComponent(status.private_key);
+
         dispatch(getTlsStatusSuccess(status));
     } catch (error) {
         dispatch(addErrorToast({ error }));
@@ -673,7 +676,11 @@ export const setTlsConfigSuccess = createAction('SET_TLS_CONFIG_SUCCESS');
 export const setTlsConfig = config => async (dispatch) => {
     dispatch(setTlsConfigRequest());
     try {
-        await apiClient.setTlsConfig(config);
+        const values = { ...config };
+        values.certificate_chain = encodeURIComponent(values.certificate_chain);
+        values.private_key = encodeURIComponent(values.private_key);
+
+        await apiClient.setTlsConfig(values);
         dispatch(setTlsConfigSuccess(config));
         dispatch(addSuccessToast('encryption_config_saved'));
     } catch (error) {
diff --git a/client/src/components/App/index.js b/client/src/components/App/index.js
index 74bc6960..5bf0f925 100644
--- a/client/src/components/App/index.js
+++ b/client/src/components/App/index.js
@@ -1,6 +1,7 @@
 import React, { Component, Fragment } from 'react';
 import { HashRouter, Route } from 'react-router-dom';
 import PropTypes from 'prop-types';
+import { Trans, withNamespaces } from 'react-i18next';
 import LoadingBar from 'react-redux-loading-bar';
 
 import 'react-table/react-table.css';
@@ -16,7 +17,7 @@ import Logs from '../../containers/Logs';
 import Footer from '../ui/Footer';
 import Toasts from '../Toasts';
 import Status from '../ui/Status';
-import Update from '../ui/Update';
+import Topline from '../ui/Topline';
 import i18n from '../../i18n';
 
 class App extends Component {
@@ -55,15 +56,22 @@ class App extends Component {
             !dashboard.processingVersions &&
             dashboard.isCoreRunning &&
             dashboard.isUpdateAvailable;
+        const isExpiringCertificate = false;
 
         return (
             <HashRouter hashType='noslash'>
                 <Fragment>
                     {updateAvailable &&
-                        <Update
-                            announcement={dashboard.announcement}
-                            announcementUrl={dashboard.announcementUrl}
-                        />
+                        <Topline type="info">
+                            {dashboard.announcement} <a href={dashboard.announcementUrl} target="_blank" rel="noopener noreferrer">Click here</a> for more info.
+                        </Topline>
+                    }
+                    {isExpiringCertificate &&
+                        <Topline type="warning">
+                            <Trans components={[<a href="#settings" key="0">link</a>]}>
+                                topline_expiring_certificate
+                            </Trans>
+                        </Topline>
                     }
                     <LoadingBar className="loading-bar" updateTime={1000} />
                     <Route component={Header} />
@@ -100,6 +108,7 @@ App.propTypes = {
     error: PropTypes.string,
     getVersion: PropTypes.func,
     changeLanguage: PropTypes.func,
+    encryption: PropTypes.object,
 };
 
-export default App;
+export default withNamespaces()(App);
diff --git a/client/src/components/Settings/Encryption/Form.js b/client/src/components/Settings/Encryption/Form.js
index 3c814fad..e58aa8f4 100644
--- a/client/src/components/Settings/Encryption/Form.js
+++ b/client/src/components/Settings/Encryption/Form.js
@@ -114,7 +114,12 @@ const Form = (props) => {
                             <Trans>encryption_certificates</Trans>
                         </label>
                         <div className="form__desc form__desc--top">
-                            <Trans>encryption_certificates_desc</Trans>
+                            <Trans
+                                values={{ link: 'letsencrypt.org' }}
+                                components={[<a href="https://letsencrypt.org/" key="0">link</a>]}
+                            >
+                                encryption_certificates_desc
+                            </Trans>
                         </div>
                         <Field
                             id="certificate_chain"
@@ -130,12 +135,14 @@ const Form = (props) => {
                                 <Trans>encryption_status</Trans>:
                             </div>
                             <div>
-                                <Trans>encryption_certificates_for</Trans>
-                                *.example.org, example.org
+                                <Trans values={{ domains: '*.example.org, example.org' }}>
+                                    encryption_certificates_for
+                                </Trans>
                             </div>
                             <div>
-                                <Trans>encryption_expire</Trans>
-                                2022-01-01
+                                <Trans values={{ date: '2022-01-01' }}>
+                                    encryption_expire
+                                </Trans>
                             </div>
                         </div>
                     </div>
diff --git a/client/src/components/ui/Update.css b/client/src/components/ui/Topline.css
similarity index 89%
rename from client/src/components/ui/Update.css
rename to client/src/components/ui/Topline.css
index ec7ec532..33c4e8fd 100644
--- a/client/src/components/ui/Update.css
+++ b/client/src/components/ui/Topline.css
@@ -1,4 +1,4 @@
-.update {
+.topline {
     position: relative;
     z-index: 102;
     margin-bottom: 0;
diff --git a/client/src/components/ui/Topline.js b/client/src/components/ui/Topline.js
new file mode 100644
index 00000000..13bfd827
--- /dev/null
+++ b/client/src/components/ui/Topline.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import './Topline.css';
+
+const Topline = props => (
+    <div className={`alert alert-${props.type} topline`}>
+        <div className="container">
+            {props.children}
+        </div>
+    </div>
+);
+
+Topline.propTypes = {
+    children: PropTypes.node.isRequired,
+    type: PropTypes.string.isRequired,
+};
+
+export default Topline;
diff --git a/client/src/components/ui/Update.js b/client/src/components/ui/Update.js
deleted file mode 100644
index 5df9df65..00000000
--- a/client/src/components/ui/Update.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import './Update.css';
-
-const Update = props => (
-    <div className="alert alert-info update">
-        <div className="container">
-            {props.announcement} <a href={props.announcementUrl} target="_blank" rel="noopener noreferrer">Click here</a> for more info.
-        </div>
-    </div>
-);
-
-Update.propTypes = {
-    announcement: PropTypes.string.isRequired,
-    announcementUrl: PropTypes.string.isRequired,
-};
-
-export default Update;
diff --git a/client/src/containers/App.js b/client/src/containers/App.js
index 905596c5..b6ce2cde 100644
--- a/client/src/containers/App.js
+++ b/client/src/containers/App.js
@@ -3,8 +3,8 @@ import * as actionCreators from '../actions';
 import App from '../components/App';
 
 const mapStateToProps = (state) => {
-    const { dashboard } = state;
-    const props = { dashboard };
+    const { dashboard, encryption } = state;
+    const props = { dashboard, encryption };
     return props;
 };