Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd24711785 | ||
|
|
304b5fac6e | ||
|
|
9cfbfe48ea | ||
|
|
9e32098b55 | ||
|
|
2a1814254c | ||
|
|
e0633bfa0d | ||
|
|
a33891a5a9 | ||
|
|
8504e4a155 | ||
|
|
390a380763 | ||
|
|
c01f80c248 | ||
|
|
7427c45c81 | ||
|
|
5175ac1dfe | ||
|
|
9e17dc73fe | ||
|
|
8843e562da | ||
|
|
5146a6c3d8 | ||
|
|
c22ee282e9 | ||
|
|
0b5ac811c4 | ||
|
|
096399fb0a | ||
|
|
e703490a1e | ||
|
|
6cd180771a | ||
|
|
beb861d707 | ||
|
|
d4f299aa40 | ||
|
|
c2b1433ed6 | ||
|
|
6157557a12 | ||
|
|
0443814f0c | ||
|
|
a5537a5664 | ||
|
|
ebb7537b2f | ||
|
|
efd1461102 | ||
|
|
7022cf1f64 | ||
|
|
6689e68a7a | ||
|
|
99423dd04f | ||
|
|
50344f3cd5 | ||
|
|
b410879ca9 | ||
|
|
d904af2d2c | ||
|
|
d28d498e03 | ||
|
|
c81cfa3af3 | ||
|
|
705932825a | ||
|
|
6ace7e72ab | ||
|
|
4d972791af | ||
|
|
3187625dc5 | ||
|
|
fb5447c92f | ||
|
|
55c24f884e | ||
|
|
5795e40573 | ||
|
|
f7652ec3c3 | ||
|
|
60b4ed034a | ||
|
|
189634657e | ||
|
|
dfcacdc392 | ||
|
|
25a22d7da9 | ||
|
|
9e574aa6c5 | ||
|
|
3144e7a721 | ||
|
|
bc3f13d360 | ||
|
|
010665284b |
@@ -37,7 +37,7 @@ As of the time of this writing the following translations exist:
|
|||||||
* Russian
|
* Russian
|
||||||
* Spanish
|
* Spanish
|
||||||
|
|
||||||
More translations are welcome. Send a pull request with a json file with translated strings. Take a look at [English](/src/i18n/en.json), [Russian](/src/i18n/ru.json), or [Simplified Chinese](/src/i18n/zh.json) translations for guidance.
|
More translations are welcome. The source file with the original English strings and context explanations is located at [/src/i18n/ex/base-en.json](/src/i18n/ex/base-en.json). Only `defaultMessage` needs to be translated. Send a pull request with a json file with translated strings. Take a look at [Spanish](/src/i18n/es.json), [Russian](/src/i18n/ru.json), or [Simplified Chinese](/src/i18n/zh.json) translations for guidance.
|
||||||
|
|
||||||
|
|
||||||
## Not done yet
|
## Not done yet
|
||||||
@@ -72,11 +72,11 @@ If you want to use the app with your own server and want web push notification t
|
|||||||
### Desktop screenshot
|
### Desktop screenshot
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="web-desktop-2.png" alt="Desktop web: full app" width=810 />
|
<img src="web-desktop.jpeg" alt="Desktop web: full app" width=810 />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Mobile screenshots
|
### Mobile screenshots
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="web-mob-contacts-1.png" alt="Mobile web: contacts" width=250 /> <img src="web-mob-chat-1.png" alt="Mobile web: chat" width=250 /> <img src="web-mob-info-1.png" alt="Mobile web: topic info" width=250 /> <img src="web-mob-new-chat-1.png" alt="Mobile web: start new chat" width=250 /> <img src="web-mob-acc-settings.png" alt="Mobile web: account settings" width=250 />
|
<img src="web-mob-contacts.png" alt="Mobile web: contacts" width=250 /> <img src="web-mob-chat.png" alt="Mobile web: chat" width=250 /> <img src="web-mob-info-1.png" alt="Mobile web: topic info" width=250 /> <img src="web-mob-new-chat.png" alt="Mobile web: start new chat" width=250 /> <img src="web-mob-acc-settings.png" alt="Mobile web: account settings" width=250 />
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -32,8 +32,11 @@ a:focus {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.red:hover,
|
a.danger {
|
||||||
a.red:focus {
|
color: #D32F2F;
|
||||||
|
}
|
||||||
|
a.danger:hover,
|
||||||
|
a.danger:focus {
|
||||||
color: #F44336;
|
color: #F44336;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,14 +63,16 @@ a.gray:focus {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flat-button .faux {
|
.flat-button.faux {
|
||||||
height: 1.5em;
|
display: inline-block;
|
||||||
line-height: 1.5em;
|
height: 1.4em;
|
||||||
|
line-height: 1.4em;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
padding-left: .5rem;
|
padding-left: .35rem;
|
||||||
padding-right: .5rem;
|
padding-right: .35rem;
|
||||||
border: 1px solid gray;
|
border: 1px solid #999;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
@@ -110,7 +115,7 @@ button {
|
|||||||
padding-left: 1.2rem;
|
padding-left: 1.2rem;
|
||||||
padding-right: 1.2rem;
|
padding-right: 1.2rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.25);
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.25);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
min-width: 5rem;
|
min-width: 5rem;
|
||||||
margin: 0 0.35rem 0 0.35rem;
|
margin: 0 0.35rem 0 0.35rem;
|
||||||
@@ -141,7 +146,6 @@ button.round.small {
|
|||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
box-shadow: 0 2px 0.35rem 0 rgba(0, 0, 0, 0.25);
|
box-shadow: 0 2px 0.35rem 0 rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
button:focus, button:hover, button:active {
|
button:focus, button:hover, button:active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
@@ -152,27 +156,19 @@ button:active.round.small {
|
|||||||
box-shadow: inset 0 2px 0.35rem rgba(0, 0, 0, 0.125);
|
box-shadow: inset 0 2px 0.35rem rgba(0, 0, 0, 0.125);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.blue {
|
button.primary {
|
||||||
background-color: #1e88e5; /* blue 600 */
|
background-color: #1e88e5; /* blue 600 */
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
button.blue:focus, button.blue:hover, button.blue:active {
|
button.primary:focus, button.primary:hover, button.primary:active {
|
||||||
background-color: #1976d2;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.blue {
|
|
||||||
background-color: #1e88e5; /* blue 600 */
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
button.blue:focus, button.blue:hover, button.blue:active {
|
|
||||||
background-color: #2962ff;
|
background-color: #2962ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.white {
|
button.secondary {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
color: #2196f3; /* blue 500 */
|
color: #2196f3; /* blue 500 */
|
||||||
}
|
}
|
||||||
button.white:focus, button.white:hover, button.blue:active {
|
button.secondary:focus, button.secondary:hover, button.secondary:active {
|
||||||
background-color: #e3f2fd;
|
background-color: #e3f2fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,11 +327,9 @@ li:hover .menuTrigger a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
li.left:hover .menuTrigger {
|
li.left:hover .menuTrigger {
|
||||||
-text-shadow: #c5e1a5 0 0 15px, #c5e1a5 0 0 5px;
|
|
||||||
background-image: radial-gradient(#c5e1a5 40%, transparent 75%);
|
background-image: radial-gradient(#c5e1a5 40%, transparent 75%);
|
||||||
}
|
}
|
||||||
li.right:hover .menuTrigger {
|
li.right:hover .menuTrigger {
|
||||||
-text-shadow: #fafafa 0 0 15px, #fafafa 0 0 5px;
|
|
||||||
background-image: radial-gradient(#fafafa 40%, transparent 75%);
|
background-image: radial-gradient(#fafafa 40%, transparent 75%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,13 +337,12 @@ li.right:hover .menuTrigger {
|
|||||||
|
|
||||||
/* Begin Alert: modal window with message and buttons. */
|
/* Begin Alert: modal window with message and buttons. */
|
||||||
div.alert-container {
|
div.alert-container {
|
||||||
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
|
||||||
-x-backdrop-filter: blur(4px);
|
|
||||||
background: rgba(0,0,0,0.33);
|
background: rgba(0,0,0,0.33);
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
@@ -491,8 +484,8 @@ div.alert .content {
|
|||||||
|
|
||||||
#topic-view {
|
#topic-view {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-x: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
overflow-x: hidden;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -837,8 +830,8 @@ i.search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.contact-box > li {
|
.contact-box > li {
|
||||||
display: flex;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
padding: 0.35rem 0.25rem 0.5rem 0.35rem;
|
padding: 0.35rem 0.25rem 0.5rem 0.35rem;
|
||||||
min-height: 3.75rem;
|
min-height: 3.75rem;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
@@ -879,10 +872,18 @@ i.search {
|
|||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact-box .contact-comment b {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-box .contact-comment .material-icons {
|
||||||
|
margin-bottom: .2rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Avatar and online-offline indicator */
|
/* Avatar and online-offline indicator */
|
||||||
.contact-box .avatar-box {
|
.contact-box .avatar-box {
|
||||||
margin-right: 0.35rem;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin-right: 0.35rem;
|
||||||
width: 3rem;
|
width: 3rem;
|
||||||
min-width: 3rem;
|
min-width: 3rem;
|
||||||
height: 3rem;
|
height: 3rem;
|
||||||
@@ -926,14 +927,13 @@ i.search {
|
|||||||
|
|
||||||
/* Online-offline indicator positioned inside avatar-box */
|
/* Online-offline indicator positioned inside avatar-box */
|
||||||
.avatar-box .online, .offline {
|
.avatar-box .online, .offline {
|
||||||
display: inline-block;
|
position: absolute;
|
||||||
width: 0.75rem;
|
width: 0.75rem;
|
||||||
min-width: 0.75rem;
|
min-width: 0.75rem;
|
||||||
height: 0.75rem;
|
height: 0.75rem;
|
||||||
min-height: 0.75rem;
|
min-height: 0.75rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0.1rem;
|
bottom: 0.1rem;
|
||||||
}
|
}
|
||||||
@@ -958,7 +958,6 @@ i.search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.contact-box .checkmark.material-icons {
|
.contact-box .checkmark.material-icons {
|
||||||
display: inline-block;
|
|
||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
@@ -1018,8 +1017,8 @@ i.search {
|
|||||||
|
|
||||||
/* Styling of avatar upload control */
|
/* Styling of avatar upload control */
|
||||||
.avatar-upload {
|
.avatar-upload {
|
||||||
display: flex;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 7rem;
|
width: 7rem;
|
||||||
@@ -1413,8 +1412,8 @@ i.search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-box .bubble {
|
.chat-box .bubble {
|
||||||
display: flex;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
max-width: 40rem;
|
max-width: 40rem;
|
||||||
min-width: 9rem;
|
min-width: 9rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -1522,7 +1521,6 @@ i.search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-box .timestamp {
|
.chat-box .timestamp {
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
color: #777;
|
color: #777;
|
||||||
@@ -1557,7 +1555,6 @@ i.search {
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 0.1rem;
|
border-radius: 0.1rem;
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-image {
|
.inline-image {
|
||||||
@@ -1577,11 +1574,11 @@ i.search {
|
|||||||
|
|
||||||
/* Broken image graphics which replaces the stock graphics. */
|
/* Broken image graphics which replaces the stock graphics. */
|
||||||
img.inline-image::before, img.image-preview::before {
|
img.inline-image::before, img.image-preview::before {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
color: #666;
|
color: #666;
|
||||||
display:flex;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -1672,7 +1669,6 @@ ul.tabbar > li > a {
|
|||||||
padding-top: 0.75rem;
|
padding-top: 0.75rem;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
padding-right: 1rem;
|
padding-right: 1rem;
|
||||||
display: block;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -1689,11 +1685,10 @@ ul.tabbar li.active > a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ul.tabbar li.active > a:after {
|
ul.tabbar li.active > a:after {
|
||||||
height:0.15rem;
|
height: 0.15rem;
|
||||||
display:block;
|
content: "";
|
||||||
content:"";
|
bottom: 0;
|
||||||
bottom:0;
|
left: 0;
|
||||||
left:0;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: #2196f3;
|
background: #2196f3;
|
||||||
animation: tabbar-border-expand 0.2s cubic-bezier(0.4, 0.0, 0.4, 1) 0s alternate forwards;
|
animation: tabbar-border-expand 0.2s cubic-bezier(0.4, 0.0, 0.4, 1) 0s alternate forwards;
|
||||||
@@ -1701,12 +1696,12 @@ ul.tabbar li.active > a:after {
|
|||||||
|
|
||||||
@keyframes tabbar-border-expand {
|
@keyframes tabbar-border-expand {
|
||||||
0% {
|
0% {
|
||||||
opacity:0;
|
opacity: 0;
|
||||||
width:0;
|
width: 0;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity:1;
|
opacity: 1;
|
||||||
width:100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* END Tabs */
|
/* END Tabs */
|
||||||
@@ -1788,10 +1783,10 @@ ul.tabbar li.active > a:after {
|
|||||||
|
|
||||||
/* Div with letter tile content */
|
/* Div with letter tile content */
|
||||||
.lettertile > div {
|
.lettertile > div {
|
||||||
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
position: absolute;
|
|
||||||
top: 0; bottom: 0; left: 0; right: 0;
|
top: 0; bottom: 0; left: 0; right: 0;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
<!-- React-Intl for translations -->
|
<!-- React-Intl for translations -->
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-intl@5/react-intl.umd.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-intl@5/react-intl.umd.js"></script>
|
||||||
<!-- Firebase for push notifications -->
|
<!-- Firebase for push notifications -->
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@7/firebase-app.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8/firebase-app.js"></script>
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@7/firebase-messaging.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8/firebase-messaging.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -24,9 +24,9 @@
|
|||||||
<link rel="stylesheet" href="css/base.min.css" />
|
<link rel="stylesheet" href="css/base.min.css" />
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react@17.0.1/umd/react.production.min.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react@17.0.1/umd/react.production.min.js"></script>
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-dom@17.0.1/umd/react-dom.production.min.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-dom@17.0.1/umd/react-dom.production.min.js"></script>
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-intl@5.10.14/react-intl.umd.min.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/react-intl@5.12.5/react-intl.umd.min.js"></script>
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@7.24.0/firebase-app.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8.2.9/firebase-app.js"></script>
|
||||||
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@7.24.0/firebase-messaging.js"></script>
|
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/firebase@8.2.9/firebase-messaging.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript><div id="noscript-message"><div id="noscript-title">Javascript support is required.</div>
|
<noscript><div id="noscript-message"><div id="noscript-title">Javascript support is required.</div>
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "tinode-webapp",
|
"name": "tinode-webapp",
|
||||||
"description": "Tinode messenger for the web",
|
"description": "Tinode messenger for the web",
|
||||||
"version": "0.17.0",
|
"version": "0.17.0-rc1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/tinode/example-react-js.git"
|
"url": "https://github.com/tinode/example-react-js.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run vers && npm run build:prod && npm run build:dev && npm run build:css && npm run build:i18n",
|
"build": "npm run vers && npm run build:css && npm run extract && npm run build:i18n && npm run build:prod && npm run build:dev",
|
||||||
"build:css": "postcss css/base.css > css/base.min.css",
|
"build:css": "postcss css/base.css > css/base.min.css",
|
||||||
"build:dev": "webpack --mode development",
|
"build:dev": "webpack --mode development",
|
||||||
"build:prod": "webpack --mode production",
|
"build:prod": "webpack --mode production",
|
||||||
"build:i18n": "node scripts/flatten-messages.js",
|
"build:i18n": "node scripts/pack-messages.js",
|
||||||
"vers": "node scripts/gen-version.js"
|
"vers": "node scripts/gen-version.js",
|
||||||
|
"extract": "formatjs extract src/**/*.jsx --out-file src/i18n/ex/base-en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'",
|
||||||
|
"compile": "formatjs compile"
|
||||||
},
|
},
|
||||||
"browserslist": "> 0.5%, not IE 11",
|
"browserslist": "> 0.5%, not IE 11",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -48,6 +50,15 @@
|
|||||||
"react",
|
"react",
|
||||||
"web"
|
"web"
|
||||||
],
|
],
|
||||||
|
"locales": [
|
||||||
|
"de",
|
||||||
|
"en",
|
||||||
|
"es",
|
||||||
|
"ko",
|
||||||
|
"ro",
|
||||||
|
"ru",
|
||||||
|
"zh"
|
||||||
|
],
|
||||||
"main": "umd/index.prod.js",
|
"main": "umd/index.prod.js",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/tinode/example-react-js/issues"
|
"url": "https://github.com/tinode/example-react-js/issues"
|
||||||
@@ -57,24 +68,27 @@
|
|||||||
"author": "Tinode Authors <info@tinode.co>",
|
"author": "Tinode Authors <info@tinode.co>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"firebase": "^7.24.0",
|
"firebase": "^8.2.9",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-intl": "^5.10.14",
|
"react-intl": "^5.12.5",
|
||||||
"tinode-sdk": "^0.16.10"
|
"tinode-sdk": "^0.17.0-rc1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.13.8",
|
||||||
"@babel/preset-env": "^7.12.11",
|
"@babel/preset-env": "^7.13.8",
|
||||||
"@babel/preset-react": "^7.12.10",
|
"@babel/preset-react": "^7.12.13",
|
||||||
|
"@formatjs/cli": "^4.2.6",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"babel-plugin-formatjs": "^9.0.2",
|
"babel-plugin-formatjs": "^10.2.6",
|
||||||
"browserslist": "^4.16.1",
|
"browserslist": "^4.16.3",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^8.1.0",
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^5.0.1",
|
||||||
"postcss": "^8.2.4",
|
"glob": "^7.1.6",
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"postcss": "^8.2.6",
|
||||||
"postcss-cli": "^8.3.1",
|
"postcss-cli": "^8.3.1",
|
||||||
"webpack": "^5.13.0",
|
"webpack": "^5.24.2",
|
||||||
"webpack-cli": "^4.3.1"
|
"webpack-cli": "^4.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,37 @@
|
|||||||
// 1.Flattens messages extracted via the React Intl Babel plugin. An error will be
|
// 1. Takes file with messages extracted via the React Intl Babel plugin and converts it into a file containing
|
||||||
// thrown if there are messages with the same `id`. The result is a file containing
|
|
||||||
// `id: message` pairs in "en" locale.
|
// `id: message` pairs in "en" locale.
|
||||||
//
|
//
|
||||||
// 2. Takes all translations and groups them into a single file.
|
// 2. Takes all translations and groups them into a single file.
|
||||||
var fs = require('fs');
|
const fs = require('fs');
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
var globSync = require('glob').sync;
|
const globSync = require('glob').sync;
|
||||||
var mkdirpSync = require('mkdirp').sync;
|
const mkdirpSync = require('mkdirp').sync;
|
||||||
|
|
||||||
|
const EXTRACTED_STRINGS = './src/i18n/ex/base-en.json';
|
||||||
const DEFAULT_TRANSLATION = 'en.json';
|
const DEFAULT_TRANSLATION = 'en.json';
|
||||||
const MESSAGES_PATTERN = './src/i18n/src/**/*.json';
|
|
||||||
const LANG_DIR = './src/i18n/';
|
const LANG_DIR = './src/i18n/';
|
||||||
const LANG_PATTERN = './src/i18n/*.json';
|
const LANG_PATTERN = './src/i18n/*.json';
|
||||||
const FINAL_FILE = './src/messages.json';
|
const FINAL_FILE = './src/messages.json';
|
||||||
|
const TRANSLATIONS = './src/translations.js'
|
||||||
|
|
||||||
const MISSING_MARKER = ':!![MISSING]!!:';
|
const MISSING_MARKER = ':!![MISSING]!!:';
|
||||||
const OBSOLETE_MARKER = ':!![OBSOLETE]!!:';
|
const OBSOLETE_MARKER = ':!![OBSOLETE]!!:';
|
||||||
|
|
||||||
// The react-intl-babel-plugin saves extracted messages
|
// The formatjs/cli saves extracted messages into a file {'message_id': {defaultMessage: 'default message text', description: '...'}, ...}.
|
||||||
// into files, one fine per component. Rewrite them into
|
// Rewrite them into a single file en.json {'message_id': 'default message text', }.
|
||||||
// a single file en.json.
|
|
||||||
let messages = globSync(MESSAGES_PATTERN)
|
|
||||||
.map((filename) => fs.readFileSync(filename, 'utf8'))
|
|
||||||
.map((file) => JSON.parse(file))
|
|
||||||
.reduce((collection, descriptors) => {
|
|
||||||
descriptors.forEach(({id, defaultMessage}) => {
|
|
||||||
if (!defaultMessage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (collection.hasOwnProperty(id)) {
|
|
||||||
// Message is already defined.
|
|
||||||
throw new Error(`Duplicate message id: ${id}`);
|
|
||||||
}
|
|
||||||
collection[id] = defaultMessage;
|
|
||||||
});
|
|
||||||
|
|
||||||
return collection;
|
// Read master-set of messages in default language.
|
||||||
}, {});
|
const baseMessages = JSON.parse(fs.readFileSync(EXTRACTED_STRINGS, 'utf8'));
|
||||||
|
// Convert.
|
||||||
|
const en = {};
|
||||||
|
for (const [id, msg] of Object.entries(baseMessages)) {
|
||||||
|
en[id] = msg.defaultMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write extracted strings into default language file.
|
||||||
let filename = path.join(LANG_DIR, DEFAULT_TRANSLATION);
|
let filename = path.join(LANG_DIR, DEFAULT_TRANSLATION);
|
||||||
mkdirpSync(path.dirname(filename));
|
mkdirpSync(path.dirname(filename));
|
||||||
fs.writeFileSync(filename, JSON.stringify(messages, null, 2)+"\n");
|
fs.writeFileSync(filename, JSON.stringify(en, null, 2)+"\n");
|
||||||
|
|
||||||
// Find messages present in en.json but missing in translations and vice versa.
|
// Find messages present in en.json but missing in translations and vice versa.
|
||||||
// Mark missing or obsolete messages as such in translation files.
|
// Mark missing or obsolete messages as such in translation files.
|
||||||
@@ -50,19 +41,24 @@ globSync(LANG_PATTERN)
|
|||||||
if (parts[parts.length-1] != DEFAULT_TRANSLATION) {
|
if (parts[parts.length-1] != DEFAULT_TRANSLATION) {
|
||||||
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
|
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
|
||||||
// Find missing messages.
|
// Find missing messages.
|
||||||
Object.entries(messages).forEach((ent) => {
|
Object.entries(baseMessages).forEach((ent) => {
|
||||||
if (!translated.hasOwnProperty(ent[0])) {
|
if (!translated.hasOwnProperty(ent[0])) {
|
||||||
|
// Missing translation.
|
||||||
translated[ent[0]] = MISSING_MARKER + ent[1];
|
translated[ent[0]] = MISSING_MARKER + ent[1];
|
||||||
|
} else if (translated[ent[0]].indexOf(OBSOLETE_MARKER) === 0) {
|
||||||
|
// No longer obsolete. Remove obsolete marker.
|
||||||
|
translated[ent[0]] = translated[ent[0]].substring(0, OBSOLETE_MARKER.length);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Find no longer needed messages.
|
// Find no longer needed messages.
|
||||||
Object.entries(translated).forEach((ent) => {
|
Object.entries(translated).forEach((ent) => {
|
||||||
if (!messages.hasOwnProperty(ent[0])) {
|
if (!baseMessages.hasOwnProperty(ent[0]) && translated.hasOwnProperty(ent[0])) {
|
||||||
let val = translated[ent[0]];
|
let val = translated[ent[0]];
|
||||||
if (val.indexOf(MISSING_MARKER) == 0) {
|
if (val.indexOf(MISSING_MARKER) === 0) {
|
||||||
|
// No longer missing.
|
||||||
val = val.substring(0, MISSING_MARKER.length);
|
val = val.substring(0, MISSING_MARKER.length);
|
||||||
}
|
}
|
||||||
if (val.indexOf(OBSOLETE_MARKER) != 0) {
|
if (val.indexOf(OBSOLETE_MARKER) !== 0) {
|
||||||
val = OBSOLETE_MARKER + val;
|
val = OBSOLETE_MARKER + val;
|
||||||
}
|
}
|
||||||
translated[ent[0]] = val;
|
translated[ent[0]] = val;
|
||||||
@@ -72,18 +68,21 @@ globSync(LANG_PATTERN)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// List of all available translations.
|
||||||
|
const languageList = {};
|
||||||
// Combine all translations into a single json {"lang_1": {...}, "lang_2: {...}"}
|
// Combine all translations into a single json {"lang_1": {...}, "lang_2: {...}"}
|
||||||
// while removing messages marked as missing or obsolete.
|
// while removing messages marked as missing or obsolete.
|
||||||
|
|
||||||
// Read all translations.
|
// Read all translations.
|
||||||
messages = globSync(LANG_PATTERN)
|
const messages = globSync(LANG_PATTERN)
|
||||||
.map((filename) => {
|
.map((filename) => {
|
||||||
const parts = filename.split('/');
|
const parts = filename.split('/');
|
||||||
const locale = parts[parts.length - 1].split('.json')[0];
|
const locale = parts[parts.length - 1].split('.json')[0];
|
||||||
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
|
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
|
||||||
|
languageList[locale] = locale;
|
||||||
// Strip messages prefixed with MISSING or OBSOLETE markers.
|
// Strip messages prefixed with MISSING or OBSOLETE markers.
|
||||||
for (let id in translated) {
|
for (let id in translated) {
|
||||||
|
// process.stdout.write("-- handling translation " + id + "; "+ String(translated[id]) + "; in " + String(filename) + "; --\n");
|
||||||
if (translated[id].indexOf(OBSOLETE_MARKER) == 0 ||
|
if (translated[id].indexOf(OBSOLETE_MARKER) == 0 ||
|
||||||
translated[id].indexOf(MISSING_MARKER) == 0) {
|
translated[id].indexOf(MISSING_MARKER) == 0) {
|
||||||
delete translated[id];
|
delete translated[id];
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
// Must be located at the root.
|
// Must be located at the root.
|
||||||
importScripts('https://cdn.jsdelivr.net/npm/firebase@7.24.0/firebase-app.js');
|
importScripts('https://cdn.jsdelivr.net/npm/firebase@8.2.9/firebase-app.js');
|
||||||
importScripts('https://cdn.jsdelivr.net/npm/firebase@7.24.0/firebase-messaging.js');
|
importScripts('https://cdn.jsdelivr.net/npm/firebase@8.2.9/firebase-messaging.js');
|
||||||
importScripts('firebase-init.js');
|
importScripts('firebase-init.js');
|
||||||
|
|
||||||
firebase.initializeApp(FIREBASE_INIT);
|
// Channel to notify the webapp.
|
||||||
|
const webAppChannel = new BroadcastChannel('tinode-sw');
|
||||||
|
|
||||||
// Basic internationalization.
|
// Basic internationalization.
|
||||||
const i18n = {
|
const i18n = {
|
||||||
@@ -44,12 +45,18 @@ self.i18nMessage = function(id) {
|
|||||||
return lang[id] || i18n['en'][id] || id;
|
return lang[id] || i18n['en'][id] || id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method shows the push notifications.
|
firebase.initializeApp(FIREBASE_INIT);
|
||||||
firebase.messaging().setBackgroundMessageHandler((payload) => {
|
const fbMessaging = firebase.messaging();
|
||||||
|
|
||||||
|
// This method shows the push notifications while the window is in background.
|
||||||
|
fbMessaging.onBackgroundMessage((payload) => {
|
||||||
if (payload.data.silent == 'true') {
|
if (payload.data.silent == 'true') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify webapp that a message was received.
|
||||||
|
webAppChannel.postMessage(payload);
|
||||||
|
|
||||||
const pushType = payload.data.what || 'msg';
|
const pushType = payload.data.what || 'msg';
|
||||||
const title = payload.data.title || self.i18nMessage(pushType == 'msg' ? 'new_message' : 'new_chat');
|
const title = payload.data.title || self.i18nMessage(pushType == 'msg' ? 'new_message' : 'new_chat');
|
||||||
const options = {
|
const options = {
|
||||||
@@ -113,23 +120,30 @@ self.addEventListener('notificationclick', event => {
|
|||||||
|
|
||||||
// This is needed for 'Add to Home Screen'.
|
// This is needed for 'Add to Home Screen'.
|
||||||
self.addEventListener('fetch', event => {
|
self.addEventListener('fetch', event => {
|
||||||
event.respondWith(
|
event.respondWith((async () => {
|
||||||
// Try to find response in cache.
|
// Try to find response in cache.
|
||||||
caches.match(event.request)
|
const cachedResponse = await caches.match(event.request);
|
||||||
.then((resp) => {
|
if (cachedResponse) {
|
||||||
// If response is found in cache, return it. Otherwise fetch.
|
return cachedResponse;
|
||||||
return resp || fetch(event.request);
|
}
|
||||||
})
|
// Not found in cache.
|
||||||
.catch((err) => {
|
const response = await fetch(event.request);
|
||||||
// Something went wrong.
|
if (!response || response.status != 200 || response.type != 'basic' || event.request.method != 'GET') {
|
||||||
console.log("Service worker Fetch:", err);
|
return response;
|
||||||
})
|
}
|
||||||
);
|
if (event.request.url && (event.request.url.startsWith('http://') || event.request.url.startsWith('https://'))) {
|
||||||
|
const cache = await caches.open(self.version);
|
||||||
|
await cache.put(event.request, response.clone());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
})());
|
||||||
});
|
});
|
||||||
|
|
||||||
// This code get the human language from the webapp for localization of strings.
|
// This code get the human language & app version from the webapp.
|
||||||
|
// Locale is used for localization of strings, version for naming the cache.
|
||||||
self.addEventListener('message', event => {
|
self.addEventListener('message', event => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
self.locale = data.locale || '';
|
self.locale = data.locale || '';
|
||||||
self.baseLocale = self.locale.toLowerCase().split(/[-_]/)[0];
|
self.baseLocale = self.locale.toLowerCase().split(/[-_]/)[0];
|
||||||
|
self.version = data.version || 'default';
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"archived_contacts": "Archivierte Kontakte ({count})",
|
"archived_contacts": "Archivierte Kontakte ({count})",
|
||||||
"contacts_not_found": "Sie haben keine Unterhaltungen<br />¯∖_(ツ)_/¯",
|
"contacts_not_found": "Sie haben keine Unterhaltungen<br />¯∖_(ツ)_/¯",
|
||||||
"full_name_prompt": "Vollständiger Name, z.B. Lisa Musterfrau",
|
"full_name_prompt": "Vollständiger Name, z.B. Lisa Musterfrau",
|
||||||
"email_prompt": "E-Mail, z.B. jdoe@beispiel.de",
|
"email_prompt": "E-Mail, z.B. lisam@beispiel.de",
|
||||||
"button_sign_up": "Anmelden",
|
"button_sign_up": "Anmelden",
|
||||||
"label_your_name": "Ihr Name",
|
"label_your_name": "Ihr Name",
|
||||||
"label_user_id": "ID:",
|
"label_user_id": "ID:",
|
||||||
|
|||||||
@@ -1,176 +1,176 @@
|
|||||||
{
|
{
|
||||||
"validate_credential_action": "confirm",
|
|
||||||
"title_tag_manager": "Tags (user discovery)",
|
|
||||||
"label_user_contacts": "Contacts:",
|
|
||||||
"button_add_another": "Add another",
|
|
||||||
"label_message_sound": "Message sound:",
|
|
||||||
"label_push_notifications": "Notification alerts:",
|
|
||||||
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
|
||||||
"label_incognito_mode": "Incognito mode:",
|
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_arning": "Are you sure you want to delete your account? It cannot be undone.",
|
|
||||||
"label_password": "Password",
|
|
||||||
"password_unchanged_prompt": "Unchanged",
|
|
||||||
"button_logout": "Logout",
|
|
||||||
"button_delete_account": "Delete account",
|
|
||||||
"label_default_access_mode": "Default access mode:",
|
|
||||||
"blocked_contacts_link": "Blocked contacts ({count})",
|
|
||||||
"link_contact_us": "Contact Us",
|
|
||||||
"link_terms_of_service": "Terms of Service",
|
|
||||||
"link_privacy_policy": "Privacy Policy",
|
|
||||||
"label_sdk": "SDK:",
|
|
||||||
"label_server_address": "Server address:",
|
|
||||||
"archived_contacts": "Archived contacts ({count})",
|
|
||||||
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
|
||||||
"full_name_prompt": "Full name, e.g. John Doe",
|
|
||||||
"email_prompt": "Email, e.g. jdoe@example.com",
|
|
||||||
"button_sign_up": "Sign up",
|
|
||||||
"label_your_name": "Your name",
|
|
||||||
"label_user_id": "ID:",
|
|
||||||
"button_edit": "Edit",
|
|
||||||
"requested_permissions": "Requested",
|
|
||||||
"granted_permissions": "Granted",
|
|
||||||
"menu_item_edit_permissions": "Edit permissions",
|
|
||||||
"label_other_user": "Other",
|
|
||||||
"action_clear_messages": "Clear Messages",
|
|
||||||
"clear_messages_warning": "Are you sure you want to clear all messages? It cannot be undone.",
|
|
||||||
"action_delete_messages": "Clear Messages for All",
|
|
||||||
"delete_messages_warning": "Are you sure you want to delete all messages for everyone? It cannot be undone.",
|
|
||||||
"action_leave_chat": "Leave Conversation",
|
|
||||||
"leave_chat_warning": "Are you sure you want to leave this conversation?",
|
|
||||||
"action_block_contact": "Block Contact",
|
"action_block_contact": "Block Contact",
|
||||||
"block_contact_warning": "Are you sure you want to block this contact?",
|
"action_cancel": "cancel",
|
||||||
"action_report_chat": "Report Conversation",
|
"action_clear_messages": "Clear Messages",
|
||||||
"report_chat_warning": "Are you sure you want to block and report this conversation?",
|
"action_delete_messages": "Clear Messages for All",
|
||||||
"title_info": "Info",
|
"action_leave_chat": "Leave Conversation",
|
||||||
"label_topic_name": "Name",
|
|
||||||
"label_private": "Private comment",
|
|
||||||
"private_editing_placeholder": "Visible to you only",
|
|
||||||
"label_muting_topic": "Muted:",
|
|
||||||
"action_more": "More",
|
"action_more": "More",
|
||||||
"label_your_permissions": "Your permissions:",
|
"action_report_chat": "Report Conversation",
|
||||||
"label_permissions": "Permissions:",
|
"archived_contacts": "Archived contacts ({count})",
|
||||||
"label_you": "You:",
|
"badge_owner": "owner",
|
||||||
"label_default_access": "Default access mode:",
|
"badge_you": "you",
|
||||||
"label_group_members": "Group members:",
|
"block_contact_warning": "Are you sure you want to block this contact?",
|
||||||
|
"blocked_contacts_link": "Blocked contacts ({count})",
|
||||||
|
"button_add_another": "Add another",
|
||||||
"button_add_members": "Add members",
|
"button_add_members": "Add members",
|
||||||
"group_has_no_members": "No members",
|
"button_cancel": "Cancel",
|
||||||
"login_prompt": "Login",
|
"button_confirm": "Confirm",
|
||||||
"password_prompt": "Password",
|
"button_create": "Create",
|
||||||
"stay_logged_in": "Stay logged in",
|
"button_delete_account": "Delete account",
|
||||||
"forgot_password_link": "Forgot password?",
|
"button_edit": "Edit",
|
||||||
"button_sign_in": "Sign in",
|
"button_logout": "Logout",
|
||||||
"label_client": "Client:",
|
"button_ok": "OK",
|
||||||
"label_server": "Server:",
|
|
||||||
"online_now": "online now",
|
|
||||||
"last_seen_timestamp": "Last seen",
|
|
||||||
"title_not_found": "Not found",
|
|
||||||
"channel": "channel",
|
|
||||||
"unnamed_topic": "Unnamed",
|
|
||||||
"messages_not_readable": "no access to messages",
|
|
||||||
"peers_messaging_disabled": "Peer's messaging is disabled.",
|
|
||||||
"enable_peers_messaging": "Enable",
|
|
||||||
"search_for_contacts": "Use search to find contacts",
|
|
||||||
"search_no_results": "Search returned no results",
|
|
||||||
"tabtitle_find_user": "find",
|
|
||||||
"tabtitle_new_group": "new group",
|
|
||||||
"tabtitle_group_by_id": "by id",
|
|
||||||
"new_password_placeholder": "Enter new password",
|
|
||||||
"label_reset_password": "Send a password reset email:",
|
|
||||||
"credential_email_prompt": "Your registration email",
|
|
||||||
"button_reset": "Reset",
|
"button_reset": "Reset",
|
||||||
"button_send_request": "Send request",
|
"button_send_request": "Send request",
|
||||||
"label_server_to_use": "Server to use:",
|
"button_sign_in": "Sign in",
|
||||||
"label_wire_transport": "Wire transport:",
|
"button_sign_up": "Sign up",
|
||||||
|
"button_subscribe": "Subscribe",
|
||||||
"button_update": "Update",
|
"button_update": "Update",
|
||||||
"sidepanel_title_login": "Sign In",
|
"cannot_initiate_file_upload": "Cannot initiate file upload.",
|
||||||
"sidepanel_title_register": "Create Account",
|
"channel": "channel",
|
||||||
"sidepanel_title_settings": "Settings",
|
"channel_prompt": "This is a channel",
|
||||||
"sidepanel_title_account_settings": "Account Settings",
|
"chat_invitation": "You are invited to start a new chat. What would you like to do?",
|
||||||
"sidepanel_title_acc_general": "General",
|
"chat_invitation_accept": "Accept",
|
||||||
"sidepanel_title_acc_security": "Security",
|
"chat_invitation_block": "Block",
|
||||||
"sidepanel_title_acc_notifications": "Notifications",
|
"chat_invitation_ignore": "Ignore",
|
||||||
"sidepanel_title_acc_support": "Support",
|
"clear_messages_warning": "Are you sure you want to clear all messages? It cannot be undone.",
|
||||||
"sidepanel_title_newtpk": "Start New Chat",
|
|
||||||
"sidepanel_title_cred": "Confirm Credentials",
|
|
||||||
"sidepanel_title_reset": "Reset Password",
|
|
||||||
"sidepanel_title_archive": "Archived Chats",
|
|
||||||
"sidepanel_title_blocked": "Blocked Chats",
|
|
||||||
"reconnect_countdown": "Disconnected. Reconnecting in {seconds}…",
|
|
||||||
"reconnect_now": "Try now",
|
|
||||||
"push_init_failed": "Failed to initialize push notifications",
|
|
||||||
"invalid_security_token": "Invalid security token",
|
|
||||||
"no_connection": "No connection",
|
|
||||||
"code_doesnot_match": "Code does not match",
|
"code_doesnot_match": "Code does not match",
|
||||||
"update_available": "Update available.",
|
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
||||||
"reload_update": "Reload",
|
"contacts_not_found_short": "No contacts match ''{query}''",
|
||||||
"phone_dative": "phone",
|
"credential_email_prompt": "Your registration email",
|
||||||
"email_dative": "email",
|
"delete_account": "Delete account",
|
||||||
"enter_confirmation_code_prompt": "Enter confirmation code sent to you by {method}:",
|
"delete_account_arning": "Are you sure you want to delete your account? It cannot be undone.",
|
||||||
"numeric_confirmation_code_prompt": "Numbers only",
|
"delete_messages_warning": "Are you sure you want to delete all messages for everyone? It cannot be undone.",
|
||||||
"button_confirm": "Confirm",
|
|
||||||
"save_attachment": "save",
|
|
||||||
"deleted_content": "content deleted",
|
"deleted_content": "content deleted",
|
||||||
|
"download_action": "download",
|
||||||
|
"email_dative": "email",
|
||||||
|
"email_prompt": "Email, e.g. jdoe@example.com",
|
||||||
|
"enable_peers_messaging": "Enable",
|
||||||
|
"enter_confirmation_code_prompt": "Enter confirmation code sent to you by {method}:",
|
||||||
|
"error_invalid_id": "Invalid ID",
|
||||||
|
"file_attachment_too_large": "The file size {size} exceeds the {limit} limit.",
|
||||||
|
"forgot_password_link": "Forgot password?",
|
||||||
|
"full_name_prompt": "Full name, e.g. John Doe",
|
||||||
|
"granted_permissions": "Granted",
|
||||||
|
"group_has_no_members": "No members",
|
||||||
|
"group_user_id_prompt": "Group or User ID",
|
||||||
|
"image_caption_prompt": "Image caption",
|
||||||
"invalid_content": "invalid content",
|
"invalid_content": "invalid content",
|
||||||
"user_not_found": "Not found",
|
"invalid_security_token": "Invalid security token",
|
||||||
"badge_you": "you",
|
"label_client": "Client:",
|
||||||
"badge_owner": "owner",
|
"label_content_type": "Content type:",
|
||||||
"menu_item_info": "Info",
|
"label_default_access": "Default access mode:",
|
||||||
|
"label_default_access_mode": "Default access mode:",
|
||||||
|
"label_file_name": "File name:",
|
||||||
|
"label_group_members": "Group members:",
|
||||||
|
"label_incognito_mode": "Incognito mode:",
|
||||||
|
"label_message_sound": "Message sound:",
|
||||||
|
"label_muting_topic": "Muted:",
|
||||||
|
"label_other_user": "Other",
|
||||||
|
"label_password": "Password",
|
||||||
|
"label_permissions": "Permissions:",
|
||||||
|
"label_private": "Private comment",
|
||||||
|
"label_push_notifications": "Notification alerts:",
|
||||||
|
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
||||||
|
"label_reset_password": "Send a password reset email:",
|
||||||
|
"label_sdk": "SDK:",
|
||||||
|
"label_server": "Server:",
|
||||||
|
"label_server_address": "Server address:",
|
||||||
|
"label_server_to_use": "Server to use:",
|
||||||
|
"label_size": "Size:",
|
||||||
|
"label_topic_name": "Name",
|
||||||
|
"label_user_contacts": "Contacts:",
|
||||||
|
"label_user_id": "ID:",
|
||||||
|
"label_wire_transport": "Wire transport:",
|
||||||
|
"label_you": "You:",
|
||||||
|
"label_your_name": "Your name",
|
||||||
|
"label_your_permissions": "Your permissions:",
|
||||||
|
"last_seen_timestamp": "Last seen",
|
||||||
|
"leave_chat_warning": "Are you sure you want to leave this conversation?",
|
||||||
|
"link_contact_us": "Contact Us",
|
||||||
|
"link_privacy_policy": "Privacy Policy",
|
||||||
|
"link_terms_of_service": "Terms of Service",
|
||||||
|
"login_prompt": "Login",
|
||||||
|
"menu_item_archive_topic": "Archive",
|
||||||
|
"menu_item_block": "Block",
|
||||||
"menu_item_clear_messages": "Clear messages",
|
"menu_item_clear_messages": "Clear messages",
|
||||||
"menu_item_clear_messages_for_all": "Clear for All",
|
"menu_item_clear_messages_for_all": "Clear for All",
|
||||||
"menu_item_delete": "Delete",
|
"menu_item_delete": "Delete",
|
||||||
"menu_item_delete_for_all": "Delete for All",
|
"menu_item_delete_for_all": "Delete for All",
|
||||||
"menu_item_send_retry": "Retry",
|
|
||||||
"menu_item_mute": "Mute",
|
|
||||||
"menu_item_unmute": "Unmute",
|
|
||||||
"menu_item_delete_topic": "Delete",
|
"menu_item_delete_topic": "Delete",
|
||||||
"topic_delete_warning": "Are you sure you want to delete this conversation?",
|
"menu_item_edit_permissions": "Edit permissions",
|
||||||
"menu_item_unblock": "Unblock",
|
"menu_item_info": "Info",
|
||||||
"menu_item_block": "Block",
|
|
||||||
"topic_block_warning": "Are you sure you want to block this conversation?",
|
|
||||||
"menu_item_member_delete": "Remove",
|
"menu_item_member_delete": "Remove",
|
||||||
"menu_item_archive_topic": "Archive",
|
"menu_item_mute": "Mute",
|
||||||
"action_cancel": "cancel",
|
"menu_item_send_retry": "Retry",
|
||||||
"upload_finishing": "finishing...",
|
"menu_item_unblock": "Unblock",
|
||||||
"no_contacts": "You have no contacts :-(",
|
"menu_item_unmute": "Unmute",
|
||||||
"contacts_not_found_short": "No contacts match ''{query}''",
|
|
||||||
"title_group_members": "Group Members",
|
|
||||||
"title_all_contacts": "All Contacts",
|
|
||||||
"button_ok": "OK",
|
|
||||||
"button_cancel": "Cancel",
|
|
||||||
"more_online_members": "+{overflow} more",
|
|
||||||
"download_action": "download",
|
|
||||||
"label_file_name": "File name:",
|
|
||||||
"label_content_type": "Content type:",
|
|
||||||
"label_size": "Size:",
|
|
||||||
"chat_invitation": "You are invited to start a new chat. What would you like to do?",
|
|
||||||
"chat_invitation_accept": "Accept",
|
|
||||||
"chat_invitation_ignore": "Ignore",
|
|
||||||
"chat_invitation_block": "Block",
|
|
||||||
"error_invalid_id": "Invalid ID",
|
|
||||||
"group_user_id_prompt": "Group or User ID",
|
|
||||||
"button_subscribe": "Subscribe",
|
|
||||||
"topic_name_editing_placeholder": "Freeform name of the group",
|
|
||||||
"channel_prompt": "This is a channel",
|
|
||||||
"button_create": "Create",
|
|
||||||
"permission_join": "Join ({val})",
|
|
||||||
"permission_read": "Read ({val})",
|
|
||||||
"permission_write": "Write ({val})",
|
|
||||||
"permission_pres": "Get notified ({val})",
|
|
||||||
"permission_admin": "Approve ({val})",
|
|
||||||
"permission_share": "Share ({val})",
|
|
||||||
"permission_delete": "Delete ({val})",
|
|
||||||
"permission_owner": "Owner ({val})",
|
|
||||||
"title_permissions": "Permissions",
|
|
||||||
"message_sending": "sending...",
|
"message_sending": "sending...",
|
||||||
"message_sending_failed": "failed",
|
"message_sending_failed": "failed",
|
||||||
"search_placeholder": "List like email:alice@example.com, tel:17025550003...",
|
"messages_not_readable": "no access to messages",
|
||||||
"messaging_disabled_prompt": "Messaging disabled",
|
"messaging_disabled_prompt": "Messaging disabled",
|
||||||
|
"more_online_members": "+{overflow} more",
|
||||||
"new_message_prompt": "New message",
|
"new_message_prompt": "New message",
|
||||||
"image_caption_prompt": "Image caption",
|
"new_password_placeholder": "Enter new password",
|
||||||
"file_attachment_too_large": "The file size {size} exceeds the {limit} limit.",
|
"no_connection": "No connection",
|
||||||
"cannot_initiate_file_upload": "Cannot initiate file upload.",
|
"no_contacts": "You have no contacts :-(",
|
||||||
"tags_not_found": "No tags defined. Add some.",
|
"numeric_confirmation_code_prompt": "Numbers only",
|
||||||
|
"online_now": "online now",
|
||||||
|
"password_prompt": "Password",
|
||||||
|
"password_unchanged_prompt": "Unchanged",
|
||||||
|
"peers_messaging_disabled": "Peer's messaging is disabled.",
|
||||||
|
"permission_admin": "Approve ({val})",
|
||||||
|
"permission_delete": "Delete ({val})",
|
||||||
|
"permission_join": "Join ({val})",
|
||||||
|
"permission_owner": "Owner ({val})",
|
||||||
|
"permission_pres": "Get notified ({val})",
|
||||||
|
"permission_read": "Read ({val})",
|
||||||
|
"permission_share": "Share ({val})",
|
||||||
|
"permission_write": "Write ({val})",
|
||||||
|
"phone_dative": "phone",
|
||||||
|
"private_editing_placeholder": "Visible to you only",
|
||||||
|
"push_init_failed": "Failed to initialize push notifications",
|
||||||
|
"reconnect_countdown": "Disconnected. Reconnecting in {seconds}…",
|
||||||
|
"reconnect_now": "Try now",
|
||||||
|
"reload_update": "Reload",
|
||||||
|
"report_chat_warning": "Are you sure you want to block and report this conversation?",
|
||||||
|
"requested_permissions": "Requested",
|
||||||
|
"save_attachment": "save",
|
||||||
|
"search_for_contacts": "Use search to find contacts",
|
||||||
|
"search_no_results": "Search returned no results",
|
||||||
|
"search_placeholder": "List like email:alice@example.com, tel:17025550003...",
|
||||||
|
"sidepanel_title_acc_general": "General",
|
||||||
|
"sidepanel_title_acc_notifications": "Notifications",
|
||||||
|
"sidepanel_title_acc_security": "Security",
|
||||||
|
"sidepanel_title_acc_support": "Support",
|
||||||
|
"sidepanel_title_account_settings": "Account Settings",
|
||||||
|
"sidepanel_title_archive": "Archived Chats",
|
||||||
|
"sidepanel_title_blocked": "Blocked Chats",
|
||||||
|
"sidepanel_title_cred": "Confirm Credentials",
|
||||||
|
"sidepanel_title_login": "Sign In",
|
||||||
|
"sidepanel_title_newtpk": "Start New Chat",
|
||||||
|
"sidepanel_title_register": "Create Account",
|
||||||
|
"sidepanel_title_reset": "Reset Password",
|
||||||
|
"sidepanel_title_settings": "Settings",
|
||||||
|
"stay_logged_in": "Stay logged in",
|
||||||
|
"tabtitle_find_user": "find",
|
||||||
|
"tabtitle_group_by_id": "by id",
|
||||||
|
"tabtitle_new_group": "new group",
|
||||||
"tags_editor_no_tags": "Add some tags",
|
"tags_editor_no_tags": "Add some tags",
|
||||||
"title_manage_tags": "Manage"
|
"tags_not_found": "No tags defined. Add some.",
|
||||||
|
"title_all_contacts": "All Contacts",
|
||||||
|
"title_group_members": "Group Members",
|
||||||
|
"title_info": "Info",
|
||||||
|
"title_manage_tags": "Manage",
|
||||||
|
"title_not_found": "Not found",
|
||||||
|
"title_permissions": "Permissions",
|
||||||
|
"title_tag_manager": "Tags (user discovery)",
|
||||||
|
"topic_block_warning": "Are you sure you want to block this conversation?",
|
||||||
|
"topic_delete_warning": "Are you sure you want to delete this conversation?",
|
||||||
|
"topic_name_editing_placeholder": "Freeform name of the group",
|
||||||
|
"unnamed_topic": "Unnamed",
|
||||||
|
"update_available": "Update available.",
|
||||||
|
"upload_finishing": "finishing...",
|
||||||
|
"user_not_found": "Not found",
|
||||||
|
"validate_credential_action": "confirm"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"private_editing_placeholder": "Sólo visible para tí",
|
"private_editing_placeholder": "Sólo visible para tí",
|
||||||
"label_muting_topic": "Silenciado:",
|
"label_muting_topic": "Silenciado:",
|
||||||
"action_more": "Más",
|
"action_more": "Más",
|
||||||
"label_your_permissions": "Tus permisoss:",
|
"label_your_permissions": "Tus permisos:",
|
||||||
"label_permissions": "Permisos:",
|
"label_permissions": "Permisos:",
|
||||||
"label_you": "Tú:",
|
"label_you": "Tú:",
|
||||||
"label_default_access": "Modo de acceso predeterminado:",
|
"label_default_access": "Modo de acceso predeterminado:",
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
"code_doesnot_match": "El código no coincide",
|
"code_doesnot_match": "El código no coincide",
|
||||||
"phone_dative": "teléfono",
|
"phone_dative": "teléfono",
|
||||||
"email_dative": "correo electrónico",
|
"email_dative": "correo electrónico",
|
||||||
"enter_confirmation_code_prompt": "Intruduzca el código de confirmación enviado a tu {method}:",
|
"enter_confirmation_code_prompt": "Introduzca el código de confirmación enviado a tu {method}:",
|
||||||
"numeric_confirmation_code_prompt": "Sólo números",
|
"numeric_confirmation_code_prompt": "Sólo números",
|
||||||
"button_confirm": "Confirmar",
|
"button_confirm": "Confirmar",
|
||||||
"save_attachment": "guardar",
|
"save_attachment": "guardar",
|
||||||
|
|||||||
@@ -0,0 +1,696 @@
|
|||||||
|
{
|
||||||
|
"action_block_contact": {
|
||||||
|
"defaultMessage": "Block Contact",
|
||||||
|
"description": "Flat button [Block Contact]"
|
||||||
|
},
|
||||||
|
"action_cancel": {
|
||||||
|
"defaultMessage": "cancel",
|
||||||
|
"description": "Call to action [cancel]"
|
||||||
|
},
|
||||||
|
"action_clear_messages": {
|
||||||
|
"defaultMessage": "Clear Messages",
|
||||||
|
"description": "Flat button [Clear Messages] (soft-delete messages)"
|
||||||
|
},
|
||||||
|
"action_delete_messages": {
|
||||||
|
"defaultMessage": "Clear Messages for All",
|
||||||
|
"description": "Flat button [Clear for All] (hard-delete all messages)"
|
||||||
|
},
|
||||||
|
"action_leave_chat": {
|
||||||
|
"defaultMessage": "Leave Conversation",
|
||||||
|
"description": "Flat button [Leave Conversation]"
|
||||||
|
},
|
||||||
|
"action_more": {
|
||||||
|
"defaultMessage": "More",
|
||||||
|
"description": "Action for showing more content"
|
||||||
|
},
|
||||||
|
"action_report_chat": {
|
||||||
|
"defaultMessage": "Report Conversation",
|
||||||
|
"description": "Flat button [Report Group]"
|
||||||
|
},
|
||||||
|
"archived_contacts": {
|
||||||
|
"defaultMessage": "Archived contacts ({count})",
|
||||||
|
"description": "Label for archived chats"
|
||||||
|
},
|
||||||
|
"badge_owner": {
|
||||||
|
"defaultMessage": "owner",
|
||||||
|
"description": "Badge for indicating the owner"
|
||||||
|
},
|
||||||
|
"badge_you": {
|
||||||
|
"defaultMessage": "you",
|
||||||
|
"description": "Badge for indicating the current user"
|
||||||
|
},
|
||||||
|
"block_contact_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to block this contact?",
|
||||||
|
"description": "Alert dialog warning when blocking a contact."
|
||||||
|
},
|
||||||
|
"blocked_contacts_link": {
|
||||||
|
"defaultMessage": "Blocked contacts ({count})",
|
||||||
|
"description": "Blocked contacts link"
|
||||||
|
},
|
||||||
|
"button_add_another": {
|
||||||
|
"defaultMessage": "Add another",
|
||||||
|
"description": "Call to action [+ add another]"
|
||||||
|
},
|
||||||
|
"button_add_members": {
|
||||||
|
"defaultMessage": "Add members",
|
||||||
|
"description": "Flat button [Add members] (to topic)"
|
||||||
|
},
|
||||||
|
"button_cancel": {
|
||||||
|
"defaultMessage": "Cancel",
|
||||||
|
"description": "Button [Cancel]"
|
||||||
|
},
|
||||||
|
"button_confirm": {
|
||||||
|
"defaultMessage": "Confirm",
|
||||||
|
"description": "Button [Confirm]"
|
||||||
|
},
|
||||||
|
"button_create": {
|
||||||
|
"defaultMessage": "Create",
|
||||||
|
"description": "Button [Create]"
|
||||||
|
},
|
||||||
|
"button_delete_account": {
|
||||||
|
"defaultMessage": "Delete account",
|
||||||
|
"description": "Button [Delete account]"
|
||||||
|
},
|
||||||
|
"button_edit": {
|
||||||
|
"defaultMessage": "Edit",
|
||||||
|
"description": "Call to action [Edit]"
|
||||||
|
},
|
||||||
|
"button_logout": {
|
||||||
|
"defaultMessage": "Logout",
|
||||||
|
"description": "Button [Logout]"
|
||||||
|
},
|
||||||
|
"button_ok": {
|
||||||
|
"defaultMessage": "OK",
|
||||||
|
"description": "Button [OK]"
|
||||||
|
},
|
||||||
|
"button_reset": {
|
||||||
|
"defaultMessage": "Reset",
|
||||||
|
"description": "Button [Reset]"
|
||||||
|
},
|
||||||
|
"button_send_request": {
|
||||||
|
"defaultMessage": "Send request",
|
||||||
|
"description": "Button [Send request]"
|
||||||
|
},
|
||||||
|
"button_sign_in": {
|
||||||
|
"defaultMessage": "Sign in",
|
||||||
|
"description": "Button [Sign In]"
|
||||||
|
},
|
||||||
|
"button_sign_up": {
|
||||||
|
"defaultMessage": "Sign up",
|
||||||
|
"description": "Create account button [Sign Up]"
|
||||||
|
},
|
||||||
|
"button_subscribe": {
|
||||||
|
"defaultMessage": "Subscribe",
|
||||||
|
"description": "Button [Subscribe]"
|
||||||
|
},
|
||||||
|
"button_update": {
|
||||||
|
"defaultMessage": "Update",
|
||||||
|
"description": "Button [Update]"
|
||||||
|
},
|
||||||
|
"cannot_initiate_file_upload": {
|
||||||
|
"defaultMessage": "Cannot initiate file upload.",
|
||||||
|
"description": "Generic error messagewhen attachment fails"
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"defaultMessage": "channel",
|
||||||
|
"description": "Subtitle shown for channels in MessagesView instead of last seen"
|
||||||
|
},
|
||||||
|
"channel_prompt": {
|
||||||
|
"defaultMessage": "This is a channel",
|
||||||
|
"description": "Checkbox label when creating a channel"
|
||||||
|
},
|
||||||
|
"chat_invitation": {
|
||||||
|
"defaultMessage": "You are invited to start a new chat. What would you like to do?",
|
||||||
|
"description": "New chat invitation message: [Accept] [Ignore] [Block]."
|
||||||
|
},
|
||||||
|
"chat_invitation_accept": {
|
||||||
|
"defaultMessage": "Accept",
|
||||||
|
"description": "Action [Accept] for chat invitation."
|
||||||
|
},
|
||||||
|
"chat_invitation_block": {
|
||||||
|
"defaultMessage": "Block",
|
||||||
|
"description": "Action [Block] for chat invitation."
|
||||||
|
},
|
||||||
|
"chat_invitation_ignore": {
|
||||||
|
"defaultMessage": "Ignore",
|
||||||
|
"description": "Action [Ignore] for chat invitation."
|
||||||
|
},
|
||||||
|
"clear_messages_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to clear all messages? It cannot be undone.",
|
||||||
|
"description": "Alert dialog warning when deleting all messages."
|
||||||
|
},
|
||||||
|
"code_doesnot_match": {
|
||||||
|
"defaultMessage": "Code does not match",
|
||||||
|
"description": "Error message when the credential validation code is incorrect."
|
||||||
|
},
|
||||||
|
"contacts_not_found": {
|
||||||
|
"defaultMessage": "You have no chats<br />¯∖_(ツ)_/¯",
|
||||||
|
"description": "HTML message shown in ContactList when no contacts are found"
|
||||||
|
},
|
||||||
|
"contacts_not_found_short": {
|
||||||
|
"defaultMessage": "No contacts match ''{query}''",
|
||||||
|
"description": "Shown in ContactsView when search returned no results"
|
||||||
|
},
|
||||||
|
"credential_email_prompt": {
|
||||||
|
"defaultMessage": "Your registration email",
|
||||||
|
"description": "Placeholder for entering email"
|
||||||
|
},
|
||||||
|
"delete_account": {
|
||||||
|
"defaultMessage": "Delete account",
|
||||||
|
"description": "Title for delete account warning"
|
||||||
|
},
|
||||||
|
"delete_account_arning": {
|
||||||
|
"defaultMessage": "Are you sure you want to delete your account? It cannot be undone.",
|
||||||
|
"description": "Warning message when deleting an account"
|
||||||
|
},
|
||||||
|
"delete_messages_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to delete all messages for everyone? It cannot be undone.",
|
||||||
|
"description": "Alert dialog warning when hard-deleting all messages."
|
||||||
|
},
|
||||||
|
"deleted_content": {
|
||||||
|
"defaultMessage": "content deleted",
|
||||||
|
"description": "Shown when messages are deleted"
|
||||||
|
},
|
||||||
|
"download_action": {
|
||||||
|
"defaultMessage": "download",
|
||||||
|
"description": "Call to action [download]"
|
||||||
|
},
|
||||||
|
"email_dative": {
|
||||||
|
"defaultMessage": "email",
|
||||||
|
"description": "Dative case of 'email', i.e. 'email' in 'by email'"
|
||||||
|
},
|
||||||
|
"email_prompt": {
|
||||||
|
"defaultMessage": "Email, e.g. jdoe@example.com",
|
||||||
|
"description": "Input placeholder for email entry"
|
||||||
|
},
|
||||||
|
"enable_peers_messaging": {
|
||||||
|
"defaultMessage": "Enable",
|
||||||
|
"description": "Call to action to enable peer's messaging"
|
||||||
|
},
|
||||||
|
"enter_confirmation_code_prompt": {
|
||||||
|
"defaultMessage": "Enter confirmation code sent to you by {method}:",
|
||||||
|
"description": "Request to enter confirmation code"
|
||||||
|
},
|
||||||
|
"error_invalid_id": {
|
||||||
|
"defaultMessage": "Invalid ID",
|
||||||
|
"description": "Error message"
|
||||||
|
},
|
||||||
|
"file_attachment_too_large": {
|
||||||
|
"defaultMessage": "The file size {size} exceeds the {limit} limit.",
|
||||||
|
"description": "Error message when attachment is too large"
|
||||||
|
},
|
||||||
|
"forgot_password_link": {
|
||||||
|
"defaultMessage": "Forgot password?",
|
||||||
|
"description": "Link to Reset password form"
|
||||||
|
},
|
||||||
|
"full_name_prompt": {
|
||||||
|
"defaultMessage": "Full name, e.g. John Doe",
|
||||||
|
"description": "Input placeholder for person's full name"
|
||||||
|
},
|
||||||
|
"granted_permissions": {
|
||||||
|
"defaultMessage": "Granted",
|
||||||
|
"description": "Title for permissions"
|
||||||
|
},
|
||||||
|
"group_has_no_members": {
|
||||||
|
"defaultMessage": "No members",
|
||||||
|
"description": "Shown in place of group members"
|
||||||
|
},
|
||||||
|
"group_user_id_prompt": {
|
||||||
|
"defaultMessage": "Group or User ID",
|
||||||
|
"description": "Prompt for entering user or group ID"
|
||||||
|
},
|
||||||
|
"image_caption_prompt": {
|
||||||
|
"defaultMessage": "Image caption",
|
||||||
|
"description": "Prompt in SendMessage for attached image"
|
||||||
|
},
|
||||||
|
"invalid_content": {
|
||||||
|
"defaultMessage": "invalid content",
|
||||||
|
"description": "Shown when message is unreadable"
|
||||||
|
},
|
||||||
|
"invalid_security_token": {
|
||||||
|
"defaultMessage": "Invalid security token",
|
||||||
|
"description": "Error message when resetting password."
|
||||||
|
},
|
||||||
|
"label_client": {
|
||||||
|
"defaultMessage": "Client:",
|
||||||
|
"description": "Label for a client version"
|
||||||
|
},
|
||||||
|
"label_content_type": {
|
||||||
|
"defaultMessage": "Content type:",
|
||||||
|
"description": "Label for file content type (mime)"
|
||||||
|
},
|
||||||
|
"label_default_access": {
|
||||||
|
"defaultMessage": "Default access mode:",
|
||||||
|
"description": "Section title"
|
||||||
|
},
|
||||||
|
"label_default_access_mode": {
|
||||||
|
"defaultMessage": "Default access mode:",
|
||||||
|
"description": "Label for default access mode"
|
||||||
|
},
|
||||||
|
"label_file_name": {
|
||||||
|
"defaultMessage": "File name:",
|
||||||
|
"description": "Label for a file name"
|
||||||
|
},
|
||||||
|
"label_group_members": {
|
||||||
|
"defaultMessage": "Group members:",
|
||||||
|
"description": "Section title or label"
|
||||||
|
},
|
||||||
|
"label_incognito_mode": {
|
||||||
|
"defaultMessage": "Incognito mode:",
|
||||||
|
"description": "Label for incognito mode toggle"
|
||||||
|
},
|
||||||
|
"label_message_sound": {
|
||||||
|
"defaultMessage": "Message sound:",
|
||||||
|
"description": "Label for message sounds toggle"
|
||||||
|
},
|
||||||
|
"label_muting_topic": {
|
||||||
|
"defaultMessage": "Muted:",
|
||||||
|
"description": "Label for Muting/unmuting the topic"
|
||||||
|
},
|
||||||
|
"label_other_user": {
|
||||||
|
"defaultMessage": "Other",
|
||||||
|
"description": "Label for the other user when the user is unnamed"
|
||||||
|
},
|
||||||
|
"label_password": {
|
||||||
|
"defaultMessage": "Password",
|
||||||
|
"description": "Label for password editing"
|
||||||
|
},
|
||||||
|
"label_permissions": {
|
||||||
|
"defaultMessage": "Permissions:",
|
||||||
|
"description": "Section title"
|
||||||
|
},
|
||||||
|
"label_private": {
|
||||||
|
"defaultMessage": "Private comment",
|
||||||
|
"description": "Label for editing 'private'"
|
||||||
|
},
|
||||||
|
"label_push_notifications": {
|
||||||
|
"defaultMessage": "Notification alerts:",
|
||||||
|
"description": "Label for push notifications switch"
|
||||||
|
},
|
||||||
|
"label_push_notifications_disabled": {
|
||||||
|
"defaultMessage": "Notification alerts (requires HTTPS):",
|
||||||
|
"description": "Label for push notifications switch"
|
||||||
|
},
|
||||||
|
"label_reset_password": {
|
||||||
|
"defaultMessage": "Send a password reset email:",
|
||||||
|
"description": "Label for password reset field"
|
||||||
|
},
|
||||||
|
"label_sdk": {
|
||||||
|
"defaultMessage": "SDK:"
|
||||||
|
},
|
||||||
|
"label_server": {
|
||||||
|
"defaultMessage": "Server:",
|
||||||
|
"description": "Label for a server version"
|
||||||
|
},
|
||||||
|
"label_server_address": {
|
||||||
|
"defaultMessage": "Server address:"
|
||||||
|
},
|
||||||
|
"label_server_to_use": {
|
||||||
|
"defaultMessage": "Server to use:",
|
||||||
|
"description": "Label for server selector in SettingsView"
|
||||||
|
},
|
||||||
|
"label_size": {
|
||||||
|
"defaultMessage": "Size:",
|
||||||
|
"description": "Label for file size"
|
||||||
|
},
|
||||||
|
"label_topic_name": {
|
||||||
|
"defaultMessage": "Name",
|
||||||
|
"description": "Label for editing topic name"
|
||||||
|
},
|
||||||
|
"label_user_contacts": {
|
||||||
|
"defaultMessage": "Contacts:",
|
||||||
|
"description": "Label for user contacts"
|
||||||
|
},
|
||||||
|
"label_user_id": {
|
||||||
|
"defaultMessage": "ID:",
|
||||||
|
"description": "Label for user address (ID)"
|
||||||
|
},
|
||||||
|
"label_wire_transport": {
|
||||||
|
"defaultMessage": "Wire transport:",
|
||||||
|
"description": "Label for wire transport selection in SettingsView"
|
||||||
|
},
|
||||||
|
"label_you": {
|
||||||
|
"defaultMessage": "You:",
|
||||||
|
"description": "Label for the current user"
|
||||||
|
},
|
||||||
|
"label_your_name": {
|
||||||
|
"defaultMessage": "Your name",
|
||||||
|
"description": "Label for full name editing"
|
||||||
|
},
|
||||||
|
"label_your_permissions": {
|
||||||
|
"defaultMessage": "Your permissions:",
|
||||||
|
"description": "Label for current user permissions"
|
||||||
|
},
|
||||||
|
"last_seen_timestamp": {
|
||||||
|
"defaultMessage": "Last seen",
|
||||||
|
"description": "Label for the timestamp of when the user or topic was last online"
|
||||||
|
},
|
||||||
|
"leave_chat_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to leave this conversation?",
|
||||||
|
"description": "Alert dialog warning when unsubscribing from a chat."
|
||||||
|
},
|
||||||
|
"link_contact_us": {
|
||||||
|
"defaultMessage": "Contact Us",
|
||||||
|
"description": "Ancor text for contacting us by email"
|
||||||
|
},
|
||||||
|
"link_privacy_policy": {
|
||||||
|
"defaultMessage": "Privacy Policy",
|
||||||
|
"description": "Ancor text for privacy policy link"
|
||||||
|
},
|
||||||
|
"link_terms_of_service": {
|
||||||
|
"defaultMessage": "Terms of Service",
|
||||||
|
"description": "Ancor text for terms of service link"
|
||||||
|
},
|
||||||
|
"login_prompt": {
|
||||||
|
"defaultMessage": "Login",
|
||||||
|
"description": "Placeholer for username/login"
|
||||||
|
},
|
||||||
|
"menu_item_archive_topic": {
|
||||||
|
"defaultMessage": "Archive",
|
||||||
|
"description": "Move topic from the list of active chats to archive"
|
||||||
|
},
|
||||||
|
"menu_item_block": {
|
||||||
|
"defaultMessage": "Block",
|
||||||
|
"description": "Block topic or user"
|
||||||
|
},
|
||||||
|
"menu_item_clear_messages": {
|
||||||
|
"defaultMessage": "Clear messages",
|
||||||
|
"description": "Delete all messages"
|
||||||
|
},
|
||||||
|
"menu_item_clear_messages_for_all": {
|
||||||
|
"defaultMessage": "Clear for All",
|
||||||
|
"description": "Delete all message(s) for all members"
|
||||||
|
},
|
||||||
|
"menu_item_delete": {
|
||||||
|
"defaultMessage": "Delete",
|
||||||
|
"description": "Delete selected messages"
|
||||||
|
},
|
||||||
|
"menu_item_delete_for_all": {
|
||||||
|
"defaultMessage": "Delete for All",
|
||||||
|
"description": "Delete selected message(s) for all members"
|
||||||
|
},
|
||||||
|
"menu_item_delete_topic": {
|
||||||
|
"defaultMessage": "Delete",
|
||||||
|
"description": "Delete entire topic"
|
||||||
|
},
|
||||||
|
"menu_item_edit_permissions": {
|
||||||
|
"defaultMessage": "Edit permissions",
|
||||||
|
"description": "Menu item [Edit permissions]"
|
||||||
|
},
|
||||||
|
"menu_item_info": {
|
||||||
|
"defaultMessage": "Info",
|
||||||
|
"description": "Show extended topic information"
|
||||||
|
},
|
||||||
|
"menu_item_member_delete": {
|
||||||
|
"defaultMessage": "Remove",
|
||||||
|
"description": "Remove user from topic"
|
||||||
|
},
|
||||||
|
"menu_item_mute": {
|
||||||
|
"defaultMessage": "Mute",
|
||||||
|
"description": "Turn off notifications"
|
||||||
|
},
|
||||||
|
"menu_item_send_retry": {
|
||||||
|
"defaultMessage": "Retry",
|
||||||
|
"description": "Retry sending message"
|
||||||
|
},
|
||||||
|
"menu_item_unblock": {
|
||||||
|
"defaultMessage": "Unblock",
|
||||||
|
"description": "Unblock topic or user"
|
||||||
|
},
|
||||||
|
"menu_item_unmute": {
|
||||||
|
"defaultMessage": "Unmute",
|
||||||
|
"description": "Turn notifications on"
|
||||||
|
},
|
||||||
|
"message_sending": {
|
||||||
|
"defaultMessage": "sending...",
|
||||||
|
"description": "Message being sent, in place of time stamp"
|
||||||
|
},
|
||||||
|
"message_sending_failed": {
|
||||||
|
"defaultMessage": "failed",
|
||||||
|
"description": "Failed to send message, in place of time stamp"
|
||||||
|
},
|
||||||
|
"messages_not_readable": {
|
||||||
|
"defaultMessage": "no access to messages",
|
||||||
|
"description": "Message shown in topic without the read access"
|
||||||
|
},
|
||||||
|
"messaging_disabled_prompt": {
|
||||||
|
"defaultMessage": "Messaging disabled",
|
||||||
|
"description": "Prompt in SendMessage in read-only topic"
|
||||||
|
},
|
||||||
|
"more_online_members": {
|
||||||
|
"defaultMessage": "+{overflow} more",
|
||||||
|
"description": "Shown in MessagesView title bar when the number of online subscribers exceeds MAX_ONLINE_IN_TOPIC"
|
||||||
|
},
|
||||||
|
"new_message_prompt": {
|
||||||
|
"defaultMessage": "New message",
|
||||||
|
"description": "Prompt in send message field"
|
||||||
|
},
|
||||||
|
"new_password_placeholder": {
|
||||||
|
"defaultMessage": "Enter new password",
|
||||||
|
"description": "Placeholder for entering new password"
|
||||||
|
},
|
||||||
|
"no_connection": {
|
||||||
|
"defaultMessage": "No connection",
|
||||||
|
"description": "Warning that the user is offline."
|
||||||
|
},
|
||||||
|
"no_contacts": {
|
||||||
|
"defaultMessage": "You have no contacts :-(",
|
||||||
|
"description": "Shown in ContactsView when the user has no contacts"
|
||||||
|
},
|
||||||
|
"numeric_confirmation_code_prompt": {
|
||||||
|
"defaultMessage": "Numbers only",
|
||||||
|
"description": "Prompt for numeric conformation code"
|
||||||
|
},
|
||||||
|
"online_now": {
|
||||||
|
"defaultMessage": "online now",
|
||||||
|
"description": "Indicator that the user or topic is currently online"
|
||||||
|
},
|
||||||
|
"password_prompt": {
|
||||||
|
"defaultMessage": "Password",
|
||||||
|
"description": "Placeholder/prompt for entering password"
|
||||||
|
},
|
||||||
|
"password_unchanged_prompt": {
|
||||||
|
"defaultMessage": "Unchanged",
|
||||||
|
"description": "Message in editor while password is unchanged"
|
||||||
|
},
|
||||||
|
"peers_messaging_disabled": {
|
||||||
|
"defaultMessage": "Peer's messaging is disabled.",
|
||||||
|
"description": "Shown when the p2p peer's messaging is disabled"
|
||||||
|
},
|
||||||
|
"permission_admin": {
|
||||||
|
"defaultMessage": "Approve ({val})",
|
||||||
|
"description": "Name of A permission"
|
||||||
|
},
|
||||||
|
"permission_delete": {
|
||||||
|
"defaultMessage": "Delete ({val})",
|
||||||
|
"description": "Name of D permission"
|
||||||
|
},
|
||||||
|
"permission_join": {
|
||||||
|
"defaultMessage": "Join ({val})",
|
||||||
|
"description": "Name of J permission"
|
||||||
|
},
|
||||||
|
"permission_owner": {
|
||||||
|
"defaultMessage": "Owner ({val})",
|
||||||
|
"description": "Name of O permission"
|
||||||
|
},
|
||||||
|
"permission_pres": {
|
||||||
|
"defaultMessage": "Get notified ({val})",
|
||||||
|
"description": "Name of P permission"
|
||||||
|
},
|
||||||
|
"permission_read": {
|
||||||
|
"defaultMessage": "Read ({val})",
|
||||||
|
"description": "Name of R permission"
|
||||||
|
},
|
||||||
|
"permission_share": {
|
||||||
|
"defaultMessage": "Share ({val})",
|
||||||
|
"description": "Name of S permission"
|
||||||
|
},
|
||||||
|
"permission_write": {
|
||||||
|
"defaultMessage": "Write ({val})",
|
||||||
|
"description": "Name of W permission"
|
||||||
|
},
|
||||||
|
"phone_dative": {
|
||||||
|
"defaultMessage": "phone",
|
||||||
|
"description": "Dative case of 'phone', i.e. 'phone' in 'by phone'"
|
||||||
|
},
|
||||||
|
"private_editing_placeholder": {
|
||||||
|
"defaultMessage": "Visible to you only",
|
||||||
|
"description": "Placeholder for editing 'private'"
|
||||||
|
},
|
||||||
|
"push_init_failed": {
|
||||||
|
"defaultMessage": "Failed to initialize push notifications",
|
||||||
|
"description": "Error message when push notifications have failed to initialize."
|
||||||
|
},
|
||||||
|
"reconnect_countdown": {
|
||||||
|
"defaultMessage": "Disconnected. Reconnecting in {seconds}…",
|
||||||
|
"description": "Message shown when an app update is available."
|
||||||
|
},
|
||||||
|
"reconnect_now": {
|
||||||
|
"defaultMessage": "Try now",
|
||||||
|
"description": "Prompt for reconnecting now"
|
||||||
|
},
|
||||||
|
"reload_update": {
|
||||||
|
"defaultMessage": "Reload",
|
||||||
|
"description": "Call to action to reload application when update is available."
|
||||||
|
},
|
||||||
|
"report_chat_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to block and report this conversation?",
|
||||||
|
"description": "Alert dialog warning when reporting a conversation for abuse"
|
||||||
|
},
|
||||||
|
"requested_permissions": {
|
||||||
|
"defaultMessage": "Requested",
|
||||||
|
"description": "Title for permissions"
|
||||||
|
},
|
||||||
|
"save_attachment": {
|
||||||
|
"defaultMessage": "save",
|
||||||
|
"description": "Call to save an attachment"
|
||||||
|
},
|
||||||
|
"search_for_contacts": {
|
||||||
|
"defaultMessage": "Use search to find contacts",
|
||||||
|
"description": "Text shown in contacts view when user entered no search query."
|
||||||
|
},
|
||||||
|
"search_no_results": {
|
||||||
|
"defaultMessage": "Search returned no results",
|
||||||
|
"description": "Text shown in contacts view when query returned no results."
|
||||||
|
},
|
||||||
|
"search_placeholder": {
|
||||||
|
"defaultMessage": "List like email:alice@example.com, tel:17025550003...",
|
||||||
|
"description": "Placeholder in contacts search field"
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_notifications": {
|
||||||
|
"defaultMessage": "Notifications",
|
||||||
|
"description": "Sidepanel title for AccNotificationsView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_security": {
|
||||||
|
"defaultMessage": "Security",
|
||||||
|
"description": "Sidepanel title for AccSecurityView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_support": {
|
||||||
|
"defaultMessage": "Support",
|
||||||
|
"description": "Sidepanel title for AccSupportView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_account_settings": {
|
||||||
|
"defaultMessage": "Account Settings",
|
||||||
|
"description": "Sidepanel title for EditAccountView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_archive": {
|
||||||
|
"defaultMessage": "Archived Chats",
|
||||||
|
"description": "Sidepanel title for ContactsView-Archive."
|
||||||
|
},
|
||||||
|
"sidepanel_title_blocked": {
|
||||||
|
"defaultMessage": "Blocked Chats",
|
||||||
|
"description": "Sidepanel title for ContactsView-Blocked."
|
||||||
|
},
|
||||||
|
"sidepanel_title_cred": {
|
||||||
|
"defaultMessage": "Confirm Credentials",
|
||||||
|
"description": "Sidepanel title for ValidationView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_login": {
|
||||||
|
"defaultMessage": "Sign In",
|
||||||
|
"description": "Sidepanel title for LoginView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_newtpk": {
|
||||||
|
"defaultMessage": "Start New Chat",
|
||||||
|
"description": "Sidepanel title for NewTopicView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_register": {
|
||||||
|
"defaultMessage": "Create Account",
|
||||||
|
"description": "Sidepanel title for CreateAccountView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_reset": {
|
||||||
|
"defaultMessage": "Reset Password",
|
||||||
|
"description": "Sidepanel title for PasswordResetView."
|
||||||
|
},
|
||||||
|
"sidepanel_title_settings": {
|
||||||
|
"defaultMessage": "Settings",
|
||||||
|
"description": "Sidepanel title for SettingsView."
|
||||||
|
},
|
||||||
|
"stay_logged_in": {
|
||||||
|
"defaultMessage": "Stay logged in",
|
||||||
|
"description": "Label for a checkbox"
|
||||||
|
},
|
||||||
|
"tabtitle_find_user": {
|
||||||
|
"defaultMessage": "find",
|
||||||
|
"description": "Tab title Find"
|
||||||
|
},
|
||||||
|
"tabtitle_group_by_id": {
|
||||||
|
"defaultMessage": "by id",
|
||||||
|
"description": "Tab title Find topic by ID"
|
||||||
|
},
|
||||||
|
"tabtitle_new_group": {
|
||||||
|
"defaultMessage": "new group",
|
||||||
|
"description": "Tab title New Group"
|
||||||
|
},
|
||||||
|
"tags_editor_no_tags": {
|
||||||
|
"defaultMessage": "Add some tags",
|
||||||
|
"description": "Tag editor prompt when no tags are found."
|
||||||
|
},
|
||||||
|
"tags_not_found": {
|
||||||
|
"defaultMessage": "No tags defined. Add some.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"title_all_contacts": {
|
||||||
|
"defaultMessage": "All Contacts",
|
||||||
|
"description": "Section title [All Contacts]"
|
||||||
|
},
|
||||||
|
"title_group_members": {
|
||||||
|
"defaultMessage": "Group Members",
|
||||||
|
"description": "Section title"
|
||||||
|
},
|
||||||
|
"title_info": {
|
||||||
|
"defaultMessage": "Info",
|
||||||
|
"description": "Title for InfoView"
|
||||||
|
},
|
||||||
|
"title_manage_tags": {
|
||||||
|
"defaultMessage": "Manage",
|
||||||
|
"description": "Section title for the list of tags"
|
||||||
|
},
|
||||||
|
"title_not_found": {
|
||||||
|
"defaultMessage": "Not found",
|
||||||
|
"description": "Title shown when topic is not found"
|
||||||
|
},
|
||||||
|
"title_permissions": {
|
||||||
|
"defaultMessage": "Permissions",
|
||||||
|
"description": "Section title"
|
||||||
|
},
|
||||||
|
"title_tag_manager": {
|
||||||
|
"defaultMessage": "Tags (user discovery)",
|
||||||
|
"description": "Section title for TagManager"
|
||||||
|
},
|
||||||
|
"topic_block_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to block this conversation?",
|
||||||
|
"description": "Alert warning when blocking a topic."
|
||||||
|
},
|
||||||
|
"topic_delete_warning": {
|
||||||
|
"defaultMessage": "Are you sure you want to delete this conversation?",
|
||||||
|
"description": "Alert warning when deleting entire topic"
|
||||||
|
},
|
||||||
|
"topic_name_editing_placeholder": {
|
||||||
|
"defaultMessage": "Freeform name of the group",
|
||||||
|
"description": "Prompt for entering topic name"
|
||||||
|
},
|
||||||
|
"unnamed_topic": {
|
||||||
|
"defaultMessage": "Unnamed",
|
||||||
|
"description": "Title shown when the topic has no name"
|
||||||
|
},
|
||||||
|
"update_available": {
|
||||||
|
"defaultMessage": "Update available.",
|
||||||
|
"description": "Message shown when an app update is available."
|
||||||
|
},
|
||||||
|
"upload_finishing": {
|
||||||
|
"defaultMessage": "finishing...",
|
||||||
|
"description": "Notification that upload is finishing"
|
||||||
|
},
|
||||||
|
"user_not_found": {
|
||||||
|
"defaultMessage": "Not found",
|
||||||
|
"description": "In place of a user's full name when the user is not found."
|
||||||
|
},
|
||||||
|
"validate_credential_action": {
|
||||||
|
"defaultMessage": "confirm",
|
||||||
|
"description": "Validate credentail call to action"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"archived_contacts": "Archivierte Kontakte ({count})",
|
"archived_contacts": "Archivierte Kontakte ({count})",
|
||||||
"contacts_not_found": "Sie haben keine Unterhaltungen<br />¯∖_(ツ)_/¯",
|
"contacts_not_found": "Sie haben keine Unterhaltungen<br />¯∖_(ツ)_/¯",
|
||||||
"full_name_prompt": "Vollständiger Name, z.B. Lisa Musterfrau",
|
"full_name_prompt": "Vollständiger Name, z.B. Lisa Musterfrau",
|
||||||
"email_prompt": "E-Mail, z.B. jdoe@beispiel.de",
|
"email_prompt": "E-Mail, z.B. lisam@beispiel.de",
|
||||||
"button_sign_up": "Anmelden",
|
"button_sign_up": "Anmelden",
|
||||||
"label_your_name": "Ihr Name",
|
"label_your_name": "Ihr Name",
|
||||||
"label_user_id": "ID:",
|
"label_user_id": "ID:",
|
||||||
@@ -176,180 +176,180 @@
|
|||||||
"channel": "Sender"
|
"channel": "Sender"
|
||||||
},
|
},
|
||||||
"en": {
|
"en": {
|
||||||
"validate_credential_action": "confirm",
|
|
||||||
"title_tag_manager": "Tags (user discovery)",
|
|
||||||
"label_user_contacts": "Contacts:",
|
|
||||||
"button_add_another": "Add another",
|
|
||||||
"label_message_sound": "Message sound:",
|
|
||||||
"label_push_notifications": "Notification alerts:",
|
|
||||||
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
|
||||||
"label_incognito_mode": "Incognito mode:",
|
|
||||||
"delete_account": "Delete account",
|
|
||||||
"delete_account_arning": "Are you sure you want to delete your account? It cannot be undone.",
|
|
||||||
"label_password": "Password",
|
|
||||||
"password_unchanged_prompt": "Unchanged",
|
|
||||||
"button_logout": "Logout",
|
|
||||||
"button_delete_account": "Delete account",
|
|
||||||
"label_default_access_mode": "Default access mode:",
|
|
||||||
"blocked_contacts_link": "Blocked contacts ({count})",
|
|
||||||
"link_contact_us": "Contact Us",
|
|
||||||
"link_terms_of_service": "Terms of Service",
|
|
||||||
"link_privacy_policy": "Privacy Policy",
|
|
||||||
"label_sdk": "SDK:",
|
|
||||||
"label_server_address": "Server address:",
|
|
||||||
"archived_contacts": "Archived contacts ({count})",
|
|
||||||
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
|
||||||
"full_name_prompt": "Full name, e.g. John Doe",
|
|
||||||
"email_prompt": "Email, e.g. jdoe@example.com",
|
|
||||||
"button_sign_up": "Sign up",
|
|
||||||
"label_your_name": "Your name",
|
|
||||||
"label_user_id": "ID:",
|
|
||||||
"button_edit": "Edit",
|
|
||||||
"requested_permissions": "Requested",
|
|
||||||
"granted_permissions": "Granted",
|
|
||||||
"menu_item_edit_permissions": "Edit permissions",
|
|
||||||
"label_other_user": "Other",
|
|
||||||
"action_clear_messages": "Clear Messages",
|
|
||||||
"clear_messages_warning": "Are you sure you want to clear all messages? It cannot be undone.",
|
|
||||||
"action_delete_messages": "Clear Messages for All",
|
|
||||||
"delete_messages_warning": "Are you sure you want to delete all messages for everyone? It cannot be undone.",
|
|
||||||
"action_leave_chat": "Leave Conversation",
|
|
||||||
"leave_chat_warning": "Are you sure you want to leave this conversation?",
|
|
||||||
"action_block_contact": "Block Contact",
|
"action_block_contact": "Block Contact",
|
||||||
"block_contact_warning": "Are you sure you want to block this contact?",
|
"action_cancel": "cancel",
|
||||||
"action_report_chat": "Report Conversation",
|
"action_clear_messages": "Clear Messages",
|
||||||
"report_chat_warning": "Are you sure you want to block and report this conversation?",
|
"action_delete_messages": "Clear Messages for All",
|
||||||
"title_info": "Info",
|
"action_leave_chat": "Leave Conversation",
|
||||||
"label_topic_name": "Name",
|
|
||||||
"label_private": "Private comment",
|
|
||||||
"private_editing_placeholder": "Visible to you only",
|
|
||||||
"label_muting_topic": "Muted:",
|
|
||||||
"action_more": "More",
|
"action_more": "More",
|
||||||
"label_your_permissions": "Your permissions:",
|
"action_report_chat": "Report Conversation",
|
||||||
"label_permissions": "Permissions:",
|
"archived_contacts": "Archived contacts ({count})",
|
||||||
"label_you": "You:",
|
"badge_owner": "owner",
|
||||||
"label_default_access": "Default access mode:",
|
"badge_you": "you",
|
||||||
"label_group_members": "Group members:",
|
"block_contact_warning": "Are you sure you want to block this contact?",
|
||||||
|
"blocked_contacts_link": "Blocked contacts ({count})",
|
||||||
|
"button_add_another": "Add another",
|
||||||
"button_add_members": "Add members",
|
"button_add_members": "Add members",
|
||||||
"group_has_no_members": "No members",
|
"button_cancel": "Cancel",
|
||||||
"login_prompt": "Login",
|
"button_confirm": "Confirm",
|
||||||
"password_prompt": "Password",
|
"button_create": "Create",
|
||||||
"stay_logged_in": "Stay logged in",
|
"button_delete_account": "Delete account",
|
||||||
"forgot_password_link": "Forgot password?",
|
"button_edit": "Edit",
|
||||||
"button_sign_in": "Sign in",
|
"button_logout": "Logout",
|
||||||
"label_client": "Client:",
|
"button_ok": "OK",
|
||||||
"label_server": "Server:",
|
|
||||||
"online_now": "online now",
|
|
||||||
"last_seen_timestamp": "Last seen",
|
|
||||||
"title_not_found": "Not found",
|
|
||||||
"channel": "channel",
|
|
||||||
"unnamed_topic": "Unnamed",
|
|
||||||
"messages_not_readable": "no access to messages",
|
|
||||||
"peers_messaging_disabled": "Peer's messaging is disabled.",
|
|
||||||
"enable_peers_messaging": "Enable",
|
|
||||||
"search_for_contacts": "Use search to find contacts",
|
|
||||||
"search_no_results": "Search returned no results",
|
|
||||||
"tabtitle_find_user": "find",
|
|
||||||
"tabtitle_new_group": "new group",
|
|
||||||
"tabtitle_group_by_id": "by id",
|
|
||||||
"new_password_placeholder": "Enter new password",
|
|
||||||
"label_reset_password": "Send a password reset email:",
|
|
||||||
"credential_email_prompt": "Your registration email",
|
|
||||||
"button_reset": "Reset",
|
"button_reset": "Reset",
|
||||||
"button_send_request": "Send request",
|
"button_send_request": "Send request",
|
||||||
"label_server_to_use": "Server to use:",
|
"button_sign_in": "Sign in",
|
||||||
"label_wire_transport": "Wire transport:",
|
"button_sign_up": "Sign up",
|
||||||
|
"button_subscribe": "Subscribe",
|
||||||
"button_update": "Update",
|
"button_update": "Update",
|
||||||
"sidepanel_title_login": "Sign In",
|
"cannot_initiate_file_upload": "Cannot initiate file upload.",
|
||||||
"sidepanel_title_register": "Create Account",
|
"channel": "channel",
|
||||||
"sidepanel_title_settings": "Settings",
|
"channel_prompt": "This is a channel",
|
||||||
"sidepanel_title_account_settings": "Account Settings",
|
"chat_invitation": "You are invited to start a new chat. What would you like to do?",
|
||||||
"sidepanel_title_acc_general": "General",
|
"chat_invitation_accept": "Accept",
|
||||||
"sidepanel_title_acc_security": "Security",
|
"chat_invitation_block": "Block",
|
||||||
"sidepanel_title_acc_notifications": "Notifications",
|
"chat_invitation_ignore": "Ignore",
|
||||||
"sidepanel_title_acc_support": "Support",
|
"clear_messages_warning": "Are you sure you want to clear all messages? It cannot be undone.",
|
||||||
"sidepanel_title_newtpk": "Start New Chat",
|
|
||||||
"sidepanel_title_cred": "Confirm Credentials",
|
|
||||||
"sidepanel_title_reset": "Reset Password",
|
|
||||||
"sidepanel_title_archive": "Archived Chats",
|
|
||||||
"sidepanel_title_blocked": "Blocked Chats",
|
|
||||||
"reconnect_countdown": "Disconnected. Reconnecting in {seconds}…",
|
|
||||||
"reconnect_now": "Try now",
|
|
||||||
"push_init_failed": "Failed to initialize push notifications",
|
|
||||||
"invalid_security_token": "Invalid security token",
|
|
||||||
"no_connection": "No connection",
|
|
||||||
"code_doesnot_match": "Code does not match",
|
"code_doesnot_match": "Code does not match",
|
||||||
"update_available": "Update available.",
|
"contacts_not_found": "You have no chats<br />¯∖_(ツ)_/¯",
|
||||||
"reload_update": "Reload",
|
"contacts_not_found_short": "No contacts match ''{query}''",
|
||||||
"phone_dative": "phone",
|
"credential_email_prompt": "Your registration email",
|
||||||
"email_dative": "email",
|
"delete_account": "Delete account",
|
||||||
"enter_confirmation_code_prompt": "Enter confirmation code sent to you by {method}:",
|
"delete_account_arning": "Are you sure you want to delete your account? It cannot be undone.",
|
||||||
"numeric_confirmation_code_prompt": "Numbers only",
|
"delete_messages_warning": "Are you sure you want to delete all messages for everyone? It cannot be undone.",
|
||||||
"button_confirm": "Confirm",
|
|
||||||
"save_attachment": "save",
|
|
||||||
"deleted_content": "content deleted",
|
"deleted_content": "content deleted",
|
||||||
|
"download_action": "download",
|
||||||
|
"email_dative": "email",
|
||||||
|
"email_prompt": "Email, e.g. jdoe@example.com",
|
||||||
|
"enable_peers_messaging": "Enable",
|
||||||
|
"enter_confirmation_code_prompt": "Enter confirmation code sent to you by {method}:",
|
||||||
|
"error_invalid_id": "Invalid ID",
|
||||||
|
"file_attachment_too_large": "The file size {size} exceeds the {limit} limit.",
|
||||||
|
"forgot_password_link": "Forgot password?",
|
||||||
|
"full_name_prompt": "Full name, e.g. John Doe",
|
||||||
|
"granted_permissions": "Granted",
|
||||||
|
"group_has_no_members": "No members",
|
||||||
|
"group_user_id_prompt": "Group or User ID",
|
||||||
|
"image_caption_prompt": "Image caption",
|
||||||
"invalid_content": "invalid content",
|
"invalid_content": "invalid content",
|
||||||
"user_not_found": "Not found",
|
"invalid_security_token": "Invalid security token",
|
||||||
"badge_you": "you",
|
"label_client": "Client:",
|
||||||
"badge_owner": "owner",
|
"label_content_type": "Content type:",
|
||||||
"menu_item_info": "Info",
|
"label_default_access": "Default access mode:",
|
||||||
|
"label_default_access_mode": "Default access mode:",
|
||||||
|
"label_file_name": "File name:",
|
||||||
|
"label_group_members": "Group members:",
|
||||||
|
"label_incognito_mode": "Incognito mode:",
|
||||||
|
"label_message_sound": "Message sound:",
|
||||||
|
"label_muting_topic": "Muted:",
|
||||||
|
"label_other_user": "Other",
|
||||||
|
"label_password": "Password",
|
||||||
|
"label_permissions": "Permissions:",
|
||||||
|
"label_private": "Private comment",
|
||||||
|
"label_push_notifications": "Notification alerts:",
|
||||||
|
"label_push_notifications_disabled": "Notification alerts (requires HTTPS):",
|
||||||
|
"label_reset_password": "Send a password reset email:",
|
||||||
|
"label_sdk": "SDK:",
|
||||||
|
"label_server": "Server:",
|
||||||
|
"label_server_address": "Server address:",
|
||||||
|
"label_server_to_use": "Server to use:",
|
||||||
|
"label_size": "Size:",
|
||||||
|
"label_topic_name": "Name",
|
||||||
|
"label_user_contacts": "Contacts:",
|
||||||
|
"label_user_id": "ID:",
|
||||||
|
"label_wire_transport": "Wire transport:",
|
||||||
|
"label_you": "You:",
|
||||||
|
"label_your_name": "Your name",
|
||||||
|
"label_your_permissions": "Your permissions:",
|
||||||
|
"last_seen_timestamp": "Last seen",
|
||||||
|
"leave_chat_warning": "Are you sure you want to leave this conversation?",
|
||||||
|
"link_contact_us": "Contact Us",
|
||||||
|
"link_privacy_policy": "Privacy Policy",
|
||||||
|
"link_terms_of_service": "Terms of Service",
|
||||||
|
"login_prompt": "Login",
|
||||||
|
"menu_item_archive_topic": "Archive",
|
||||||
|
"menu_item_block": "Block",
|
||||||
"menu_item_clear_messages": "Clear messages",
|
"menu_item_clear_messages": "Clear messages",
|
||||||
"menu_item_clear_messages_for_all": "Clear for All",
|
"menu_item_clear_messages_for_all": "Clear for All",
|
||||||
"menu_item_delete": "Delete",
|
"menu_item_delete": "Delete",
|
||||||
"menu_item_delete_for_all": "Delete for All",
|
"menu_item_delete_for_all": "Delete for All",
|
||||||
"menu_item_send_retry": "Retry",
|
|
||||||
"menu_item_mute": "Mute",
|
|
||||||
"menu_item_unmute": "Unmute",
|
|
||||||
"menu_item_delete_topic": "Delete",
|
"menu_item_delete_topic": "Delete",
|
||||||
"topic_delete_warning": "Are you sure you want to delete this conversation?",
|
"menu_item_edit_permissions": "Edit permissions",
|
||||||
"menu_item_unblock": "Unblock",
|
"menu_item_info": "Info",
|
||||||
"menu_item_block": "Block",
|
|
||||||
"topic_block_warning": "Are you sure you want to block this conversation?",
|
|
||||||
"menu_item_member_delete": "Remove",
|
"menu_item_member_delete": "Remove",
|
||||||
"menu_item_archive_topic": "Archive",
|
"menu_item_mute": "Mute",
|
||||||
"action_cancel": "cancel",
|
"menu_item_send_retry": "Retry",
|
||||||
"upload_finishing": "finishing...",
|
"menu_item_unblock": "Unblock",
|
||||||
"no_contacts": "You have no contacts :-(",
|
"menu_item_unmute": "Unmute",
|
||||||
"contacts_not_found_short": "No contacts match ''{query}''",
|
|
||||||
"title_group_members": "Group Members",
|
|
||||||
"title_all_contacts": "All Contacts",
|
|
||||||
"button_ok": "OK",
|
|
||||||
"button_cancel": "Cancel",
|
|
||||||
"more_online_members": "+{overflow} more",
|
|
||||||
"download_action": "download",
|
|
||||||
"label_file_name": "File name:",
|
|
||||||
"label_content_type": "Content type:",
|
|
||||||
"label_size": "Size:",
|
|
||||||
"chat_invitation": "You are invited to start a new chat. What would you like to do?",
|
|
||||||
"chat_invitation_accept": "Accept",
|
|
||||||
"chat_invitation_ignore": "Ignore",
|
|
||||||
"chat_invitation_block": "Block",
|
|
||||||
"error_invalid_id": "Invalid ID",
|
|
||||||
"group_user_id_prompt": "Group or User ID",
|
|
||||||
"button_subscribe": "Subscribe",
|
|
||||||
"topic_name_editing_placeholder": "Freeform name of the group",
|
|
||||||
"channel_prompt": "This is a channel",
|
|
||||||
"button_create": "Create",
|
|
||||||
"permission_join": "Join ({val})",
|
|
||||||
"permission_read": "Read ({val})",
|
|
||||||
"permission_write": "Write ({val})",
|
|
||||||
"permission_pres": "Get notified ({val})",
|
|
||||||
"permission_admin": "Approve ({val})",
|
|
||||||
"permission_share": "Share ({val})",
|
|
||||||
"permission_delete": "Delete ({val})",
|
|
||||||
"permission_owner": "Owner ({val})",
|
|
||||||
"title_permissions": "Permissions",
|
|
||||||
"message_sending": "sending...",
|
"message_sending": "sending...",
|
||||||
"message_sending_failed": "failed",
|
"message_sending_failed": "failed",
|
||||||
"search_placeholder": "List like email:alice@example.com, tel:17025550003...",
|
"messages_not_readable": "no access to messages",
|
||||||
"messaging_disabled_prompt": "Messaging disabled",
|
"messaging_disabled_prompt": "Messaging disabled",
|
||||||
|
"more_online_members": "+{overflow} more",
|
||||||
"new_message_prompt": "New message",
|
"new_message_prompt": "New message",
|
||||||
"image_caption_prompt": "Image caption",
|
"new_password_placeholder": "Enter new password",
|
||||||
"file_attachment_too_large": "The file size {size} exceeds the {limit} limit.",
|
"no_connection": "No connection",
|
||||||
"cannot_initiate_file_upload": "Cannot initiate file upload.",
|
"no_contacts": "You have no contacts :-(",
|
||||||
"tags_not_found": "No tags defined. Add some.",
|
"numeric_confirmation_code_prompt": "Numbers only",
|
||||||
|
"online_now": "online now",
|
||||||
|
"password_prompt": "Password",
|
||||||
|
"password_unchanged_prompt": "Unchanged",
|
||||||
|
"peers_messaging_disabled": "Peer's messaging is disabled.",
|
||||||
|
"permission_admin": "Approve ({val})",
|
||||||
|
"permission_delete": "Delete ({val})",
|
||||||
|
"permission_join": "Join ({val})",
|
||||||
|
"permission_owner": "Owner ({val})",
|
||||||
|
"permission_pres": "Get notified ({val})",
|
||||||
|
"permission_read": "Read ({val})",
|
||||||
|
"permission_share": "Share ({val})",
|
||||||
|
"permission_write": "Write ({val})",
|
||||||
|
"phone_dative": "phone",
|
||||||
|
"private_editing_placeholder": "Visible to you only",
|
||||||
|
"push_init_failed": "Failed to initialize push notifications",
|
||||||
|
"reconnect_countdown": "Disconnected. Reconnecting in {seconds}…",
|
||||||
|
"reconnect_now": "Try now",
|
||||||
|
"reload_update": "Reload",
|
||||||
|
"report_chat_warning": "Are you sure you want to block and report this conversation?",
|
||||||
|
"requested_permissions": "Requested",
|
||||||
|
"save_attachment": "save",
|
||||||
|
"search_for_contacts": "Use search to find contacts",
|
||||||
|
"search_no_results": "Search returned no results",
|
||||||
|
"search_placeholder": "List like email:alice@example.com, tel:17025550003...",
|
||||||
|
"sidepanel_title_acc_general": "General",
|
||||||
|
"sidepanel_title_acc_notifications": "Notifications",
|
||||||
|
"sidepanel_title_acc_security": "Security",
|
||||||
|
"sidepanel_title_acc_support": "Support",
|
||||||
|
"sidepanel_title_account_settings": "Account Settings",
|
||||||
|
"sidepanel_title_archive": "Archived Chats",
|
||||||
|
"sidepanel_title_blocked": "Blocked Chats",
|
||||||
|
"sidepanel_title_cred": "Confirm Credentials",
|
||||||
|
"sidepanel_title_login": "Sign In",
|
||||||
|
"sidepanel_title_newtpk": "Start New Chat",
|
||||||
|
"sidepanel_title_register": "Create Account",
|
||||||
|
"sidepanel_title_reset": "Reset Password",
|
||||||
|
"sidepanel_title_settings": "Settings",
|
||||||
|
"stay_logged_in": "Stay logged in",
|
||||||
|
"tabtitle_find_user": "find",
|
||||||
|
"tabtitle_group_by_id": "by id",
|
||||||
|
"tabtitle_new_group": "new group",
|
||||||
"tags_editor_no_tags": "Add some tags",
|
"tags_editor_no_tags": "Add some tags",
|
||||||
"title_manage_tags": "Manage"
|
"tags_not_found": "No tags defined. Add some.",
|
||||||
|
"title_all_contacts": "All Contacts",
|
||||||
|
"title_group_members": "Group Members",
|
||||||
|
"title_info": "Info",
|
||||||
|
"title_manage_tags": "Manage",
|
||||||
|
"title_not_found": "Not found",
|
||||||
|
"title_permissions": "Permissions",
|
||||||
|
"title_tag_manager": "Tags (user discovery)",
|
||||||
|
"topic_block_warning": "Are you sure you want to block this conversation?",
|
||||||
|
"topic_delete_warning": "Are you sure you want to delete this conversation?",
|
||||||
|
"topic_name_editing_placeholder": "Freeform name of the group",
|
||||||
|
"unnamed_topic": "Unnamed",
|
||||||
|
"update_available": "Update available.",
|
||||||
|
"upload_finishing": "finishing...",
|
||||||
|
"user_not_found": "Not found",
|
||||||
|
"validate_credential_action": "confirm"
|
||||||
},
|
},
|
||||||
"es": {
|
"es": {
|
||||||
"validate_credential_action": "confirmar",
|
"validate_credential_action": "confirmar",
|
||||||
@@ -401,7 +401,7 @@
|
|||||||
"private_editing_placeholder": "Sólo visible para tí",
|
"private_editing_placeholder": "Sólo visible para tí",
|
||||||
"label_muting_topic": "Silenciado:",
|
"label_muting_topic": "Silenciado:",
|
||||||
"action_more": "Más",
|
"action_more": "Más",
|
||||||
"label_your_permissions": "Tus permisoss:",
|
"label_your_permissions": "Tus permisos:",
|
||||||
"label_permissions": "Permisos:",
|
"label_permissions": "Permisos:",
|
||||||
"label_you": "Tú:",
|
"label_you": "Tú:",
|
||||||
"label_default_access": "Modo de acceso predeterminado:",
|
"label_default_access": "Modo de acceso predeterminado:",
|
||||||
@@ -458,7 +458,7 @@
|
|||||||
"code_doesnot_match": "El código no coincide",
|
"code_doesnot_match": "El código no coincide",
|
||||||
"phone_dative": "teléfono",
|
"phone_dative": "teléfono",
|
||||||
"email_dative": "correo electrónico",
|
"email_dative": "correo electrónico",
|
||||||
"enter_confirmation_code_prompt": "Intruduzca el código de confirmación enviado a tu {method}:",
|
"enter_confirmation_code_prompt": "Introduzca el código de confirmación enviado a tu {method}:",
|
||||||
"numeric_confirmation_code_prompt": "Sólo números",
|
"numeric_confirmation_code_prompt": "Sólo números",
|
||||||
"button_confirm": "Confirmar",
|
"button_confirm": "Confirmar",
|
||||||
"save_attachment": "guardar",
|
"save_attachment": "guardar",
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
// This is a generated file. Don't edit.
|
// This is a generated file. Don't edit.
|
||||||
|
|
||||||
export const PACKAGE_VERSION = "0.17.0";
|
export const PACKAGE_VERSION = "0.17.0-rc1";
|
||||||
|
|||||||
@@ -135,8 +135,10 @@ export default class AccGeneralView extends React.Component {
|
|||||||
<div className="scrollable-panel">
|
<div className="scrollable-panel">
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<label className="small"><FormattedMessage id="label_your_name" /></label>
|
<label className="small"><FormattedMessage id="label_your_name" defaultMessage="Your name"
|
||||||
<div><FormattedMessage id="full_name_prompt">{
|
description="Label for full name editing" /></label>
|
||||||
|
<div><FormattedMessage id="full_name_prompt" defaultMessage="Full name, e.g. John Doe"
|
||||||
|
description="Input placeholder for person's full name">{
|
||||||
(full_name_placeholder) => <InPlaceEdit
|
(full_name_placeholder) => <InPlaceEdit
|
||||||
placeholder={full_name_placeholder}
|
placeholder={full_name_placeholder}
|
||||||
value={this.state.fullName}
|
value={this.state.fullName}
|
||||||
|
|||||||
@@ -110,11 +110,11 @@ class AccSecurityView extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="hr" />
|
<div className="hr" />
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<a href="#" className="red flat-button" onClick={(e) => {e.preventDefault(); this.props.onLogout();}}>
|
<a href="#" className="danger flat-button" onClick={(e) => {e.preventDefault(); this.props.onLogout();}}>
|
||||||
<i className="material-icons">exit_to_app</i> <FormattedMessage id="button_logout"
|
<i className="material-icons">exit_to_app</i> <FormattedMessage id="button_logout"
|
||||||
defaultMessage="Logout" description="Button [Logout]" />
|
defaultMessage="Logout" description="Button [Logout]" />
|
||||||
</a>
|
</a>
|
||||||
<a href="#" className="red flat-button" onClick={(e) => {this.handleDeleteAccount(e);}}>
|
<a href="#" className="danger flat-button" onClick={(e) => {this.handleDeleteAccount(e);}}>
|
||||||
<i className="material-icons">delete</i> <FormattedMessage id="button_delete_account"
|
<i className="material-icons">delete</i> <FormattedMessage id="button_delete_account"
|
||||||
defaultMessage="Delete account" description="Button [Delete account]" />
|
defaultMessage="Delete account" description="Button [Delete account]" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export default class AccSupportView extends React.PureComponent {
|
|||||||
<div className="hr" />
|
<div className="hr" />
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<label className="small"><FormattedMessage id="label_client" /></label>
|
<label className="small"><FormattedMessage id="label_client" defaultMessage="Client:"
|
||||||
|
description="Label for a client version" /></label>
|
||||||
{APP_NAME}
|
{APP_NAME}
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
@@ -35,7 +36,8 @@ export default class AccSupportView extends React.PureComponent {
|
|||||||
{Tinode.getLibrary()}
|
{Tinode.getLibrary()}
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<label className="small"><FormattedMessage id="label_server" /></label>
|
<label className="small"><FormattedMessage id="label_server" defaultMessage="Server:"
|
||||||
|
description="Label for a server version" /></label>
|
||||||
{this.props.serverVersion}
|
{this.props.serverVersion}
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default class CreateAccountView extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let submitClasses = 'blue';
|
let submitClasses = 'primary';
|
||||||
if (this.props.disabled) {
|
if (this.props.disabled) {
|
||||||
submitClasses += ' disabled';
|
submitClasses += ' disabled';
|
||||||
}
|
}
|
||||||
@@ -79,11 +79,13 @@ export default class CreateAccountView extends React.PureComponent {
|
|||||||
<form className="panel-form-column" onSubmit={this.handleSubmit}>
|
<form className="panel-form-column" onSubmit={this.handleSubmit}>
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<FormattedMessage id="login_prompt">{
|
<FormattedMessage id="login_prompt" defaultMessage="Login"
|
||||||
|
description="Placeholer for username/login">{
|
||||||
(login_prompt) => <input type="text" placeholder={login_prompt} autoComplete="user-name"
|
(login_prompt) => <input type="text" placeholder={login_prompt} autoComplete="user-name"
|
||||||
value={this.state.login} onChange={this.handleLoginChange} required autoFocus />
|
value={this.state.login} onChange={this.handleLoginChange} required autoFocus />
|
||||||
}</FormattedMessage>
|
}</FormattedMessage>
|
||||||
<FormattedMessage id="password_prompt">{
|
<FormattedMessage id="password_prompt" defaultMessage="Password"
|
||||||
|
description="Placeholder/prompt for entering password">{
|
||||||
(password_prompt) => <VisiblePassword placeholder={password_prompt} autoComplete="new-password"
|
(password_prompt) => <VisiblePassword placeholder={password_prompt} autoComplete="new-password"
|
||||||
value={this.state.password} onFinished={this.handlePasswordChange}
|
value={this.state.password} onFinished={this.handlePasswordChange}
|
||||||
required={true} />
|
required={true} />
|
||||||
@@ -110,7 +112,8 @@ export default class CreateAccountView extends React.PureComponent {
|
|||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<CheckBox id="save-token" name="save-token" checked={this.state.saveToken}
|
<CheckBox id="save-token" name="save-token" checked={this.state.saveToken}
|
||||||
onChange={this.handleToggleSaveToken} />
|
onChange={this.handleToggleSaveToken} />
|
||||||
<FormattedMessage id="stay_logged_in">{
|
<FormattedMessage id="stay_logged_in" defaultMessage="Stay logged in"
|
||||||
|
description="Label for a checkbox">{
|
||||||
(stay_logged_in) => <label htmlFor="save-token"> {stay_logged_in}</label>
|
(stay_logged_in) => <label htmlFor="save-token"> {stay_logged_in}</label>
|
||||||
}</FormattedMessage>
|
}</FormattedMessage>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,13 +48,16 @@ export default class EditAccountView extends React.Component {
|
|||||||
<div className="hr" />
|
<div className="hr" />
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('notif');}}>
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('notif');}}>
|
||||||
<i className="material-icons">notifications</i> <FormattedMessage id="sidepanel_title_acc_notifications" />
|
<i className="material-icons">notifications</i> <FormattedMessage id="sidepanel_title_acc_notifications"
|
||||||
|
defaultMessage="Notifications" description="Sidepanel title for AccNotificationsView." />
|
||||||
</a>
|
</a>
|
||||||
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('security');}}>
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('security');}}>
|
||||||
<i className="material-icons">security</i> <FormattedMessage id="sidepanel_title_acc_security" />
|
<i className="material-icons">security</i> <FormattedMessage id="sidepanel_title_acc_security"
|
||||||
|
defaultMessage="Security" description="Sidepanel title for AccSecurityView." />
|
||||||
</a>
|
</a>
|
||||||
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('support');}}>
|
<a href="#" className="flat-button" onClick={(e) => {e.preventDefault(); this.props.onBasicNavigate('support');}}>
|
||||||
<i className="material-icons">contact_support</i> <FormattedMessage id="sidepanel_title_acc_support" />
|
<i className="material-icons">contact_support</i> <FormattedMessage id="sidepanel_title_acc_support"
|
||||||
|
defaultMessage="Support" description="Sidepanel title for AccSupportView." />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -615,7 +615,8 @@ class InfoView extends React.Component {
|
|||||||
{this.state.moreInfoExpanded ?
|
{this.state.moreInfoExpanded ?
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<label><FormattedMessage id="label_user_id" /></label>
|
<label><FormattedMessage id="label_user_id" defaultMessage="ID:"
|
||||||
|
description="Label for user address (ID)" /></label>
|
||||||
<tt>{this.state.address}</tt>
|
<tt>{this.state.address}</tt>
|
||||||
</div>
|
</div>
|
||||||
{this.state.groupTopic ?
|
{this.state.groupTopic ?
|
||||||
@@ -692,7 +693,8 @@ class InfoView extends React.Component {
|
|||||||
<div className="hr" />
|
<div className="hr" />
|
||||||
{this.state.owner ?
|
{this.state.owner ?
|
||||||
<>
|
<>
|
||||||
<FormattedMessage id="title_tag_manager">{
|
<FormattedMessage id="title_tag_manager" defaultMessage="Tags (user discovery)"
|
||||||
|
description="Section title for TagManager">{
|
||||||
(tags) => <TagManager
|
(tags) => <TagManager
|
||||||
title={tags}
|
title={tags}
|
||||||
tags={this.state.tags}
|
tags={this.state.tags}
|
||||||
@@ -715,18 +717,18 @@ class InfoView extends React.Component {
|
|||||||
:
|
:
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
<a href="#" className="red flat-button" onClick={this.handleLeave}>
|
<a href="#" className="danger flat-button" onClick={this.handleLeave}>
|
||||||
<i className="material-icons">exit_to_app</i> {formatMessage(messages.leave_chat)}
|
<i className="material-icons">exit_to_app</i> {formatMessage(messages.leave_chat)}
|
||||||
</a>
|
</a>
|
||||||
{!this.state.groupTopic ?
|
{!this.state.groupTopic ?
|
||||||
<a href="#" className="red flat-button" onClick={this.handleBlock}>
|
<a href="#" className="danger flat-button" onClick={this.handleBlock}>
|
||||||
<i className="material-icons">block</i> {formatMessage(messages.block_contact)}
|
<i className="material-icons">block</i> {formatMessage(messages.block_contact)}
|
||||||
</a>
|
</a>
|
||||||
:
|
:
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
{!this.state.owner ?
|
{!this.state.owner ?
|
||||||
<a href="#" className="red flat-button" onClick={this.handleReport}>
|
<a href="#" className="danger flat-button" onClick={this.handleReport}>
|
||||||
<i className="material-icons">report</i> {formatMessage(messages.report_chat)}
|
<i className="material-icons">report</i> {formatMessage(messages.report_chat)}
|
||||||
</a>
|
</a>
|
||||||
:
|
:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default class LoginView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var submitClasses = 'blue';
|
let submitClasses = 'primary';
|
||||||
if (this.props.disabled) {
|
if (this.props.disabled) {
|
||||||
submitClasses += ' disabled';
|
submitClasses += ' disabled';
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ export default class LoginView extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<form id="login-form" onSubmit={this.handleSubmit}>
|
<form id="login-form" onSubmit={this.handleSubmit}>
|
||||||
<FormattedMessage id="login_prompt" defaultMessage="Login"
|
<FormattedMessage id="login_prompt" defaultMessage="Login"
|
||||||
description="Login placeholder in LoginView">
|
description="Placeholer for username/login">
|
||||||
{
|
{
|
||||||
(login_prompt) => <input type="text" id="inputLogin"
|
(login_prompt) => <input type="text" id="inputLogin"
|
||||||
placeholder={login_prompt}
|
placeholder={login_prompt}
|
||||||
@@ -63,7 +63,7 @@ export default class LoginView extends React.Component {
|
|||||||
}
|
}
|
||||||
</FormattedMessage>
|
</FormattedMessage>
|
||||||
<FormattedMessage id="password_prompt" defaultMessage="Password"
|
<FormattedMessage id="password_prompt" defaultMessage="Password"
|
||||||
description="Password placeholder in LoginView">
|
description="Placeholder/prompt for entering password">
|
||||||
{
|
{
|
||||||
(password_prompt) => <VisiblePassword type="password" id="inputPassword"
|
(password_prompt) => <VisiblePassword type="password" id="inputPassword"
|
||||||
placeholder={password_prompt}
|
placeholder={password_prompt}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ export default class LogoView extends React.PureComponent {
|
|||||||
<img id="logo" alt="logo" src="img/logo.svg" />
|
<img id="logo" alt="logo" src="img/logo.svg" />
|
||||||
<h2>Tinode Web</h2>
|
<h2>Tinode Web</h2>
|
||||||
</a>
|
</a>
|
||||||
<p><FormattedMessage id="label_client" defaultMessage="Client:" /> {version}</p>
|
<p><FormattedMessage id="label_client" defaultMessage="Client:"
|
||||||
<p><FormattedMessage id="label_server" defaultMessage="Server:" /> {this.props.serverVersion} ({this.props.serverAddress})</p>
|
description="Label for a client version" /> {version}</p>
|
||||||
|
<p><FormattedMessage id="label_server" defaultMessage="Server:"
|
||||||
|
description="Label for a server version" /> {this.props.serverVersion} ({this.props.serverAddress})</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,25 +26,30 @@ import { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';
|
|||||||
const NOTIFICATION_EXEC_INTERVAL = 300;
|
const NOTIFICATION_EXEC_INTERVAL = 300;
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
'online_now': {
|
online_now: {
|
||||||
id: 'online_now',
|
id: 'online_now',
|
||||||
defaultMessage: 'online now',
|
defaultMessage: 'online now',
|
||||||
description: 'Indicator that the user or topic is currently online',
|
description: 'Indicator that the user or topic is currently online',
|
||||||
},
|
},
|
||||||
'last_seen': {
|
last_seen: {
|
||||||
id: 'last_seen_timestamp',
|
id: 'last_seen_timestamp',
|
||||||
defaultMessage: 'Last seen',
|
defaultMessage: 'Last seen',
|
||||||
description: 'Label for the timestamp of when the user or topic was last online'
|
description: 'Label for the timestamp of when the user or topic was last online'
|
||||||
},
|
},
|
||||||
'not_found': {
|
not_found: {
|
||||||
id: 'title_not_found',
|
id: 'title_not_found',
|
||||||
defaultMessage: 'Not found',
|
defaultMessage: 'Not found',
|
||||||
description: 'Title shown when topic is not found'
|
description: 'Title shown when topic is not found'
|
||||||
},
|
},
|
||||||
'channel': {
|
channel: {
|
||||||
id: 'channel',
|
id: 'channel',
|
||||||
defaultMessage: 'channel',
|
defaultMessage: 'channel',
|
||||||
description: 'Subtitle shown for channels in MessagesView instead of last seen'
|
description: 'Subtitle shown for channels in MessagesView instead of last seen'
|
||||||
|
},
|
||||||
|
file_attachment_too_large: {
|
||||||
|
id: 'file_attachment_too_large',
|
||||||
|
defaultMessage: 'The file size {size} exceeds the {limit} limit.',
|
||||||
|
description: 'Error message when attachment is too large'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -128,7 +133,7 @@ class MessagesView extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;
|
||||||
if (this.state.topic != prevState.topic) {
|
if (this.state.topic != prevState.topic) {
|
||||||
if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {
|
if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {
|
||||||
this.leave(prevState.topic);
|
this.leave(prevState.topic);
|
||||||
@@ -511,7 +516,7 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
// Aknowledge messages except own messages. They are
|
// Aknowledge messages except own messages. They are
|
||||||
// automatically assumed to be read and recived.
|
// automatically assumed to be read and recived.
|
||||||
const status = topic.msgStatus(msg);
|
const status = topic.msgStatus(msg, true);
|
||||||
if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
|
if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {
|
||||||
this.postReadNotification(msg.seq);
|
this.postReadNotification(msg.seq);
|
||||||
}
|
}
|
||||||
@@ -665,7 +670,7 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
if (file.size > maxExternAttachmentSize) {
|
if (file.size > maxExternAttachmentSize) {
|
||||||
// Too large.
|
// Too large.
|
||||||
this.props.onError(this.props.intl.formatMessage({id: 'file_attachment_too_large'},
|
this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large,
|
||||||
{size: bytesToHumanSize(file.size), limit: bytesToHumanSize(maxExternAttachmentSize)}), 'err');
|
{size: bytesToHumanSize(file.size), limit: bytesToHumanSize(maxExternAttachmentSize)}), 'err');
|
||||||
} else {
|
} else {
|
||||||
this.setState({ docPreview: {
|
this.setState({ docPreview: {
|
||||||
@@ -844,7 +849,7 @@ class MessagesView extends React.Component {
|
|||||||
previousFrom = thisFrom;
|
previousFrom = thisFrom;
|
||||||
|
|
||||||
const isReply = !(thisFrom == this.props.myUserId);
|
const isReply = !(thisFrom == this.props.myUserId);
|
||||||
const deliveryStatus = topic.msgStatus(msg);
|
const deliveryStatus = topic.msgStatus(msg, true);
|
||||||
|
|
||||||
let userName, userAvatar, userFrom;
|
let userName, userAvatar, userFrom;
|
||||||
if (groupTopic) {
|
if (groupTopic) {
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default class PasswordResetView extends React.PureComponent {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button className="blue" type="submit">{reset ?
|
<button className="primary" type="submit">{reset ?
|
||||||
<FormattedMessage id="button_reset" defaultMessage="Reset" description="Button [Reset]" /> :
|
<FormattedMessage id="button_reset" defaultMessage="Reset" description="Button [Reset]" /> :
|
||||||
<FormattedMessage id="button_send_request" defaultMessage="Send request"
|
<FormattedMessage id="button_send_request" defaultMessage="Send request"
|
||||||
description="Button [Send request]" />
|
description="Button [Send request]" />
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default class SettingsView extends React.PureComponent {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button type="submit" className="blue">
|
<button type="submit" className="primary">
|
||||||
<FormattedMessage id="button_update" defaultMessage="Update"
|
<FormattedMessage id="button_update" defaultMessage="Update"
|
||||||
description="Button [Update]" />
|
description="Button [Update]" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -20,67 +20,67 @@ import ValidationView from './validation-view.jsx';
|
|||||||
|
|
||||||
// Panel titles for translation.
|
// Panel titles for translation.
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
'login': {
|
login: {
|
||||||
id: 'sidepanel_title_login',
|
id: 'sidepanel_title_login',
|
||||||
description: 'Sidepanel title for LoginView.',
|
description: 'Sidepanel title for LoginView.',
|
||||||
defaultMessage: 'Sign In'
|
defaultMessage: 'Sign In'
|
||||||
},
|
},
|
||||||
'register': {
|
register: {
|
||||||
id: 'sidepanel_title_register',
|
id: 'sidepanel_title_register',
|
||||||
description: 'Sidepanel title for CreateAccountView.',
|
description: 'Sidepanel title for CreateAccountView.',
|
||||||
defaultMessage: 'Create Account'
|
defaultMessage: 'Create Account'
|
||||||
},
|
},
|
||||||
'settings': {
|
settings: {
|
||||||
id: 'sidepanel_title_settings',
|
id: 'sidepanel_title_settings',
|
||||||
description: 'Sidepanel title for SettingsView.',
|
description: 'Sidepanel title for SettingsView.',
|
||||||
defaultMessage: 'Settings'
|
defaultMessage: 'Settings'
|
||||||
},
|
},
|
||||||
'edit': {
|
edit: {
|
||||||
id: 'sidepanel_title_account_settings',
|
id: 'sidepanel_title_account_settings',
|
||||||
description: 'Sidepanel title for EditAccountView.',
|
description: 'Sidepanel title for EditAccountView.',
|
||||||
defaultMessage: 'Account Settings'
|
defaultMessage: 'Account Settings'
|
||||||
},
|
},
|
||||||
'general': {
|
general: {
|
||||||
id: 'sidepanel_title_acc_general',
|
id: 'sidepanel_title_acc_general',
|
||||||
description: 'Sidepanel title for AccGeneralView.',
|
description: 'Sidepanel title for AccGeneralView.',
|
||||||
defaultMessage: 'General'
|
defaultMessage: 'General'
|
||||||
},
|
},
|
||||||
'security': {
|
security: {
|
||||||
id: 'sidepanel_title_acc_security',
|
id: 'sidepanel_title_acc_security',
|
||||||
description: 'Sidepanel title for AccSecurityView.',
|
description: 'Sidepanel title for AccSecurityView.',
|
||||||
defaultMessage: 'Security'
|
defaultMessage: 'Security'
|
||||||
},
|
},
|
||||||
'notif': {
|
notif: {
|
||||||
id: 'sidepanel_title_acc_notifications',
|
id: 'sidepanel_title_acc_notifications',
|
||||||
description: 'Sidepanel title for AccNotificationsView.',
|
description: 'Sidepanel title for AccNotificationsView.',
|
||||||
defaultMessage: 'Notifications'
|
defaultMessage: 'Notifications'
|
||||||
},
|
},
|
||||||
'support': {
|
support: {
|
||||||
id: 'sidepanel_title_acc_support',
|
id: 'sidepanel_title_acc_support',
|
||||||
description: 'Sidepanel title for AccSupportView.',
|
description: 'Sidepanel title for AccSupportView.',
|
||||||
defaultMessage: 'Support'
|
defaultMessage: 'Support'
|
||||||
},
|
},
|
||||||
'newtpk': {
|
newtpk: {
|
||||||
id: 'sidepanel_title_newtpk',
|
id: 'sidepanel_title_newtpk',
|
||||||
description: 'Sidepanel title for NewTopicView.',
|
description: 'Sidepanel title for NewTopicView.',
|
||||||
defaultMessage: 'Start New Chat'
|
defaultMessage: 'Start New Chat'
|
||||||
},
|
},
|
||||||
'cred': {
|
cred: {
|
||||||
id: 'sidepanel_title_cred',
|
id: 'sidepanel_title_cred',
|
||||||
description: 'Sidepanel title for ValidationView.',
|
description: 'Sidepanel title for ValidationView.',
|
||||||
defaultMessage: 'Confirm Credentials'
|
defaultMessage: 'Confirm Credentials'
|
||||||
},
|
},
|
||||||
'reset': {
|
reset: {
|
||||||
id: 'sidepanel_title_reset',
|
id: 'sidepanel_title_reset',
|
||||||
description: 'Sidepanel title for PasswordResetView.',
|
description: 'Sidepanel title for PasswordResetView.',
|
||||||
defaultMessage: 'Reset Password'
|
defaultMessage: 'Reset Password'
|
||||||
},
|
},
|
||||||
'archive': {
|
archive: {
|
||||||
id: 'sidepanel_title_archive',
|
id: 'sidepanel_title_archive',
|
||||||
description: 'Sidepanel title for ContactsView-Archive.',
|
description: 'Sidepanel title for ContactsView-Archive.',
|
||||||
defaultMessage: 'Archived Chats'
|
defaultMessage: 'Archived Chats'
|
||||||
},
|
},
|
||||||
'blocked': {
|
blocked: {
|
||||||
id: 'sidepanel_title_blocked',
|
id: 'sidepanel_title_blocked',
|
||||||
description: 'Sidepanel title for ContactsView-Blocked.',
|
description: 'Sidepanel title for ContactsView-Blocked.',
|
||||||
defaultMessage: 'Blocked Chats'
|
defaultMessage: 'Blocked Chats'
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import SidepanelView from './sidepanel-view.jsx';
|
|||||||
|
|
||||||
import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED,
|
import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED,
|
||||||
MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';
|
MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';
|
||||||
|
import { PACKAGE_VERSION } from '../version.js';
|
||||||
import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';
|
import { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';
|
import { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';
|
||||||
import LocalStorageUtil from '../lib/local-storage.js';
|
import LocalStorageUtil from '../lib/local-storage.js';
|
||||||
@@ -57,6 +58,11 @@ const messages = defineMessages({
|
|||||||
id: 'code_doesnot_match',
|
id: 'code_doesnot_match',
|
||||||
defaultMessage: 'Code does not match',
|
defaultMessage: 'Code does not match',
|
||||||
description: 'Error message when the credential validation code is incorrect.'
|
description: 'Error message when the credential validation code is incorrect.'
|
||||||
|
},
|
||||||
|
menu_item_info: {
|
||||||
|
id: 'menu_item_info',
|
||||||
|
defaultMessage: 'Info',
|
||||||
|
description: 'Show extended topic information'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,9 +110,10 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handleCredAdd = this.handleCredAdd.bind(this);
|
this.handleCredAdd = this.handleCredAdd.bind(this);
|
||||||
this.handleCredDelete = this.handleCredDelete.bind(this);
|
this.handleCredDelete = this.handleCredDelete.bind(this);
|
||||||
this.handleCredConfirm = this.handleCredConfirm.bind(this);
|
this.handleCredConfirm = this.handleCredConfirm.bind(this);
|
||||||
this.initDesktopAlerts = this.initDesktopAlerts.bind(this);
|
this.initFCMessaging = this.initFCMessaging.bind(this);
|
||||||
this.togglePushToken = this.togglePushToken.bind(this);
|
this.togglePushToken = this.togglePushToken.bind(this);
|
||||||
this.requestPushToken = this.requestPushToken.bind(this);
|
this.requestPushToken = this.requestPushToken.bind(this);
|
||||||
|
this.handlePushMessage = this.handlePushMessage.bind(this);
|
||||||
this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);
|
this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);
|
||||||
this.handleStartTopicRequest = this.handleStartTopicRequest.bind(this);
|
this.handleStartTopicRequest = this.handleStartTopicRequest.bind(this);
|
||||||
this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);
|
this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);
|
||||||
@@ -134,6 +141,7 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
getBlankState() {
|
getBlankState() {
|
||||||
const settings = LocalStorageUtil.getObject('settings') || {};
|
const settings = LocalStorageUtil.getObject('settings') || {};
|
||||||
|
const persist = !!LocalStorageUtil.getObject('keep-logged-in');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connected: false,
|
connected: false,
|
||||||
@@ -147,11 +155,11 @@ class TinodeWeb extends React.Component {
|
|||||||
// "On" is the default, so saving the "off" state.
|
// "On" is the default, so saving the "off" state.
|
||||||
messageSounds: !settings.messageSoundsOff,
|
messageSounds: !settings.messageSoundsOff,
|
||||||
incognitoMode: false,
|
incognitoMode: false,
|
||||||
desktopAlerts: settings.desktopAlerts,
|
desktopAlerts: persist && settings.desktopAlerts,
|
||||||
desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&
|
desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&
|
||||||
(typeof firebase != 'undefined') && (typeof navigator != 'undefined') &&
|
(typeof firebase != 'undefined') && (typeof navigator != 'undefined') &&
|
||||||
(typeof FIREBASE_INIT != 'undefined'),
|
(typeof FIREBASE_INIT != 'undefined'),
|
||||||
firebaseToken: LocalStorageUtil.getObject('firebase-token'),
|
firebaseToken: persist ? LocalStorageUtil.getObject('firebase-token') : null,
|
||||||
|
|
||||||
applicationVisible: !document.hidden,
|
applicationVisible: !document.hidden,
|
||||||
|
|
||||||
@@ -195,7 +203,9 @@ class TinodeWeb extends React.Component {
|
|||||||
// Merged results of a search query and p2p chats.
|
// Merged results of a search query and p2p chats.
|
||||||
searchableContacts: [],
|
searchableContacts: [],
|
||||||
credMethod: undefined,
|
credMethod: undefined,
|
||||||
credCode: undefined
|
credCode: undefined,
|
||||||
|
// Topic to go to after login.
|
||||||
|
requestedTopic: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +214,10 @@ class TinodeWeb extends React.Component {
|
|||||||
window.addEventListener('online', (e) => { this.handleOnline(true); });
|
window.addEventListener('online', (e) => { this.handleOnline(true); });
|
||||||
window.addEventListener('offline', (e) => { this.handleOnline(false); });
|
window.addEventListener('offline', (e) => { this.handleOnline(false); });
|
||||||
window.addEventListener('hashchange', this.handleHashRoute);
|
window.addEventListener('hashchange', this.handleHashRoute);
|
||||||
|
// Process background notifications from the service worker.
|
||||||
|
const serviceWorkerChannel = new BroadcastChannel('tinode-sw');
|
||||||
|
serviceWorkerChannel.addEventListener('message', this.handlePushMessage);
|
||||||
|
|
||||||
// Window/tab visible or invisible for pausing timers.
|
// Window/tab visible or invisible for pausing timers.
|
||||||
document.addEventListener('visibilitychange', this.handleVisibilityEvent);
|
document.addEventListener('visibilitychange', this.handleVisibilityEvent);
|
||||||
|
|
||||||
@@ -214,39 +228,27 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
const keepLoggedIn = LocalStorageUtil.getObject('keep-logged-in');
|
const keepLoggedIn = LocalStorageUtil.getObject('keep-logged-in');
|
||||||
|
|
||||||
const {formatMessage, locale} = this.props.intl;
|
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport, locale, keepLoggedIn, resolve);
|
this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport,
|
||||||
|
this.props.intl.locale, keepLoggedIn, resolve);
|
||||||
this.tinode.onConnect = this.handleConnected;
|
this.tinode.onConnect = this.handleConnected;
|
||||||
this.tinode.onDisconnect = this.handleDisconnect;
|
this.tinode.onDisconnect = this.handleDisconnect;
|
||||||
this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;
|
this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Initialize desktop alerts.
|
// Initialize desktop alerts.
|
||||||
if (this.state.desktopAlertsEnabled) {
|
if (this.state.desktopAlertsEnabled) {
|
||||||
try {
|
this.initFCMessaging().then(() => {
|
||||||
this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging();
|
|
||||||
this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey);
|
|
||||||
navigator.serviceWorker.register('/service-worker.js').then((reg) => {
|
|
||||||
this.checkForAppUpdate(reg);
|
|
||||||
this.fbPush.useServiceWorker(reg);
|
|
||||||
reg.active.postMessage(JSON.stringify({locale: locale}));
|
|
||||||
this.initDesktopAlerts();
|
|
||||||
if (this.state.desktopAlerts) {
|
if (this.state.desktopAlerts) {
|
||||||
if (!this.state.firebaseToken) {
|
if (!this.state.firebaseToken) {
|
||||||
this.togglePushToken(true);
|
this.togglePushToken(true);
|
||||||
} else {
|
} else {
|
||||||
this.tinode.setDeviceToken(this.state.firebaseToken, true);
|
this.tinode.setDeviceToken(this.state.firebaseToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch(() => {
|
||||||
// registration failed :(
|
// do nothing: handled earlier.
|
||||||
console.log("Failed to register service worker:", err);
|
// catch needed to pervent unnecessary logging of error.
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
this.handleError(formatMessage(messages.push_init_failed), 'err');
|
|
||||||
console.log("Failed to initialize push notifications", err);
|
|
||||||
this.setState({desktopAlertsEnabled: false});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = keepLoggedIn ? LocalStorageUtil.getObject('auth-token') : undefined;
|
const token = keepLoggedIn ? LocalStorageUtil.getObject('auth-token') : undefined;
|
||||||
@@ -267,6 +269,8 @@ class TinodeWeb extends React.Component {
|
|||||||
parsedNav.path[0] = '';
|
parsedNav.path[0] = '';
|
||||||
HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsedNav.path, parsedNav.params));
|
HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsedNav.path, parsedNav.params));
|
||||||
} else if (!parsedNav.params.token) {
|
} else if (!parsedNav.params.token) {
|
||||||
|
// No token, save possible topic name and navigating to blank state.
|
||||||
|
this.setState({requestedTopic: parsedNav.path[1]});
|
||||||
HashNavigation.navigateTo('');
|
HashNavigation.navigateTo('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +296,51 @@ class TinodeWeb extends React.Component {
|
|||||||
return tinode;
|
return tinode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notifiy Tinode that a push message was received from the server.
|
||||||
|
handlePushMessage(payload) {
|
||||||
|
if (payload.data.what == 'msg' && Tinode.isChannelTopicName(payload.data.topic)) {
|
||||||
|
// The last argument is a fake user Id: otherwise the update is seen as one from the current user.
|
||||||
|
this.tinode.oobNotification(payload.data.topic, payload.data.seq, 'fake-uid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initFCMessaging() {
|
||||||
|
const {formatMessage, locale} = this.props.intl;
|
||||||
|
const onError = (msg, err) => {
|
||||||
|
console.log(msg, err);
|
||||||
|
this.handleError(formatMessage(messages.push_init_failed), 'err');
|
||||||
|
this.setState({desktopAlertsEnabled: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging();
|
||||||
|
this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey);
|
||||||
|
|
||||||
|
return navigator.serviceWorker.register('/service-worker.js').then((reg) => {
|
||||||
|
this.checkForAppUpdate(reg);
|
||||||
|
this.fbPush.useServiceWorker(reg);
|
||||||
|
reg.active.postMessage(JSON.stringify({locale: locale, version: PACKAGE_VERSION}));
|
||||||
|
// Google could not be bothered to mention that
|
||||||
|
// onTokenRefresh is never called.
|
||||||
|
this.fbPush.onTokenRefresh(() => {
|
||||||
|
this.requestPushToken();
|
||||||
|
});
|
||||||
|
|
||||||
|
// FCM pushes for channels have to be handled even when the app is in the foreground.
|
||||||
|
this.fbPush.onMessage(this.handlePushMessage);
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
}).catch((err) => {
|
||||||
|
// registration failed :(
|
||||||
|
onError("Failed to register service worker:", err);
|
||||||
|
return new Promise.reject(err);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
onError("Failed to initialize push notifications", err);
|
||||||
|
return new Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleResize() {
|
handleResize() {
|
||||||
const mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;
|
const mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -364,6 +413,8 @@ class TinodeWeb extends React.Component {
|
|||||||
handleOnline(online) {
|
handleOnline(online) {
|
||||||
if (online) {
|
if (online) {
|
||||||
this.handleError();
|
this.handleError();
|
||||||
|
clearInterval(this.reconnectCountdown);
|
||||||
|
this.tinode.reconnect();
|
||||||
} else {
|
} else {
|
||||||
this.handleError(this.props.intl.formatMessage(messages.no_connection), 'warn');
|
this.handleError(this.props.intl.formatMessage(messages.no_connection), 'warn');
|
||||||
}
|
}
|
||||||
@@ -529,6 +580,8 @@ class TinodeWeb extends React.Component {
|
|||||||
if (LocalStorageUtil.getObject('keep-logged-in')) {
|
if (LocalStorageUtil.getObject('keep-logged-in')) {
|
||||||
LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());
|
LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToTopic = this.state.requestedTopic;
|
||||||
// Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.
|
// Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.
|
||||||
const me = this.tinode.getMeTopic();
|
const me = this.tinode.getMeTopic();
|
||||||
me.onMetaDesc = this.tnMeMetaDesc;
|
me.onMetaDesc = this.tnMeMetaDesc;
|
||||||
@@ -540,6 +593,7 @@ class TinodeWeb extends React.Component {
|
|||||||
credCode: undefined,
|
credCode: undefined,
|
||||||
myUserId: this.tinode.getCurrentUserID(),
|
myUserId: this.tinode.getCurrentUserID(),
|
||||||
autoLogin: true,
|
autoLogin: true,
|
||||||
|
requestedTopic: undefined,
|
||||||
});
|
});
|
||||||
// Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.
|
// Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.
|
||||||
me.subscribe(
|
me.subscribe(
|
||||||
@@ -557,7 +611,11 @@ class TinodeWeb extends React.Component {
|
|||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({loadSpinnerVisible: false});
|
this.setState({loadSpinnerVisible: false});
|
||||||
});
|
});
|
||||||
HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts'));
|
let urlHash = HashNavigation.setUrlSidePanel(window.location.hash, 'contacts');
|
||||||
|
if (goToTopic) {
|
||||||
|
urlHash = HashNavigation.setUrlTopic(urlHash, goToTopic);
|
||||||
|
}
|
||||||
|
HashNavigation.navigateTo(urlHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
tnMeMetaDesc(desc) {
|
tnMeMetaDesc(desc) {
|
||||||
@@ -669,6 +727,10 @@ class TinodeWeb extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tinode.getMeTopic().contacts((c) => {
|
this.tinode.getMeTopic().contacts((c) => {
|
||||||
|
if (!c.topic && !c.user) {
|
||||||
|
// Contacts expect c.topic to be set.
|
||||||
|
c.topic = c.name;
|
||||||
|
}
|
||||||
newState.chatList.push(c);
|
newState.chatList.push(c);
|
||||||
if (this.state.topicSelected == c.topic) {
|
if (this.state.topicSelected == c.topic) {
|
||||||
newState.topicSelectedOnline = c.online;
|
newState.topicSelectedOnline = c.online;
|
||||||
@@ -683,7 +745,7 @@ class TinodeWeb extends React.Component {
|
|||||||
// Sending "received" notifications
|
// Sending "received" notifications
|
||||||
tnData(data) {
|
tnData(data) {
|
||||||
const topic = this.tinode.getTopic(data.topic);
|
const topic = this.tinode.getTopic(data.topic);
|
||||||
if (topic.msgStatus(data) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {
|
if (topic.msgStatus(data, true) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {
|
||||||
clearTimeout(this.receivedTimer);
|
clearTimeout(this.receivedTimer);
|
||||||
this.receivedTimer = setTimeout(() => {
|
this.receivedTimer = setTimeout(() => {
|
||||||
this.receivedTimer = undefined;
|
this.receivedTimer = undefined;
|
||||||
@@ -961,42 +1023,37 @@ class TinodeWeb extends React.Component {
|
|||||||
this.state.myUserId ? 'blocked' : ''));
|
this.state.myUserId ? 'blocked' : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize desktop alerts = push notifications.
|
|
||||||
initDesktopAlerts() {
|
|
||||||
// Google could not be bothered to mention that
|
|
||||||
// onTokenRefresh is never called.
|
|
||||||
this.fbPush.onTokenRefresh(() => {
|
|
||||||
this.requestPushToken();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.fbPush.onMessage((payload) => {
|
|
||||||
// No need to do anything about it.
|
|
||||||
// All the magic happends in the service worker.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
togglePushToken(enabled) {
|
togglePushToken(enabled) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!this.state.firebaseToken) {
|
if (!this.state.firebaseToken) {
|
||||||
this.fbPush.requestPermission().then(() => {
|
const fcm = this.fbPush ?
|
||||||
|
new Promise().resolve() :
|
||||||
|
this.initFCMessaging();
|
||||||
|
fcm.then(() => {
|
||||||
|
return this.fbPush.requestPermission();
|
||||||
|
}).then(() => {
|
||||||
this.requestPushToken();
|
this.requestPushToken();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.log("Failed to get notification permission.", err);
|
||||||
this.handleError(err.message, 'err');
|
this.handleError(err.message, 'err');
|
||||||
this.setState({desktopAlerts: false, firebaseToken: null});
|
this.setState({desktopAlerts: false, firebaseToken: null});
|
||||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
||||||
console.log("Failed to get permission to notify.", err);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({desktopAlerts: true});
|
this.setState({desktopAlerts: true});
|
||||||
|
if (LocalStorageUtil.getObject('keep-logged-in')) {
|
||||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: true});
|
LocalStorageUtil.updateObject('settings', {desktopAlerts: true});
|
||||||
}
|
}
|
||||||
} else if (this.state.firebaseToken) {
|
}
|
||||||
|
} else if (this.state.firebaseToken && this.fbPush) {
|
||||||
this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => {
|
this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => {
|
||||||
console.log("Unable to delete token.", err);
|
console.log("Unable to delete token.", err);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
LocalStorageUtil.updateObject('settings', {desktopAlerts: false});
|
||||||
localStorage.removeItem('firebase-token');
|
localStorage.removeItem('firebase-token');
|
||||||
this.setState({desktopAlerts: false, firebaseToken: null});
|
this.setState({desktopAlerts: false, firebaseToken: null});
|
||||||
|
// Inform the server that the token was deleted.
|
||||||
|
this.tinode.setDeviceToken(null);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({desktopAlerts: false, firebaseToken: null});
|
this.setState({desktopAlerts: false, firebaseToken: null});
|
||||||
@@ -1006,12 +1063,17 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
requestPushToken() {
|
requestPushToken() {
|
||||||
this.fbPush.getToken().then((refreshedToken) => {
|
this.fbPush.getToken().then((refreshedToken) => {
|
||||||
|
const persist = LocalStorageUtil.getObject('keep-logged-in');
|
||||||
if (refreshedToken != this.state.firebaseToken) {
|
if (refreshedToken != this.state.firebaseToken) {
|
||||||
this.tinode.setDeviceToken(refreshedToken, true);
|
this.tinode.setDeviceToken(refreshedToken);
|
||||||
|
if (persist) {
|
||||||
LocalStorageUtil.setObject('firebase-token', refreshedToken);
|
LocalStorageUtil.setObject('firebase-token', refreshedToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.setState({firebaseToken: refreshedToken, desktopAlerts: true});
|
this.setState({firebaseToken: refreshedToken, desktopAlerts: true});
|
||||||
|
if (persist) {
|
||||||
LocalStorageUtil.updateObject('settings', {desktopAlerts: true});
|
LocalStorageUtil.updateObject('settings', {desktopAlerts: true});
|
||||||
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.handleError(err.message, 'err');
|
this.handleError(err.message, 'err');
|
||||||
console.log("Failed to retrieve firebase token", err);
|
console.log("Failed to retrieve firebase token", err);
|
||||||
@@ -1271,7 +1333,7 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
subscribed ? {
|
subscribed ? {
|
||||||
title: this.props.intl.formatMessage({id: 'menu_item_info'}),
|
title: this.props.intl.formatMessage(messages.menu_item_info),
|
||||||
handler: this.handleShowInfoView
|
handler: this.handleShowInfoView
|
||||||
} : null,
|
} : null,
|
||||||
subscribed ? 'messages_clear' : null,
|
subscribed ? 'messages_clear' : null,
|
||||||
|
|||||||
@@ -78,11 +78,11 @@ class ValidationView extends React.PureComponent {
|
|||||||
}</FormattedMessage>
|
}</FormattedMessage>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button className="blue" onClick={this.handleSubmit}>
|
<button className="primary" onClick={this.handleSubmit}>
|
||||||
<FormattedMessage id="button_confirm" defaultMessage="Confirm" description="Button [Confirm]" />
|
<FormattedMessage id="button_confirm" defaultMessage="Confirm" description="Button [Confirm]" />
|
||||||
</button>
|
</button>
|
||||||
<button className="white" onClick={this.handleCancel}>
|
<button className="secondary" onClick={this.handleCancel}>
|
||||||
<FormattedMessage id="button_cancel" />
|
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ export default class Alert extends React.PureComponent {
|
|||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
{this.props.onReject ?
|
{this.props.onReject ?
|
||||||
<button className="outline" onClick={this.props.onReject}>
|
<button className="outline" onClick={this.props.onReject}>
|
||||||
{this.props.reject || <FormattedMessage id="button_cancel" />}
|
{this.props.reject || <FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />}
|
||||||
</button>
|
</button>
|
||||||
:
|
:
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
<button className="blue" onClick={this.props.onConfirm}>
|
<button className="primary" onClick={this.props.onConfirm}>
|
||||||
{this.props.confirm || <FormattedMessage id="button_ok" />}
|
{this.props.confirm || <FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default class ChatMessage extends React.Component {
|
|||||||
(this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));
|
(this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));
|
||||||
const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';
|
const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';
|
||||||
const avatar = this.props.deleted ? null : (this.props.userAvatar || true);
|
const avatar = this.props.deleted ? null : (this.props.userAvatar || true);
|
||||||
const fullDisplay = (this.props.userFrom && this.props.response &&
|
const fullDisplay = (this.props.userFrom && this.props.response && !this.props.deleted &&
|
||||||
(this.props.sequence == 'single' || this.props.sequence == 'last'));
|
(this.props.sequence == 'single' || this.props.sequence == 'last'));
|
||||||
|
|
||||||
let content = this.props.content;
|
let content = this.props.content;
|
||||||
|
|||||||
@@ -48,14 +48,14 @@ class ContactList extends React.Component {
|
|||||||
// If filter function is provided, filter out the items
|
// If filter function is provided, filter out the items
|
||||||
// which don't satisfy the condition.
|
// which don't satisfy the condition.
|
||||||
if (this.props.filterFunc && this.props.filter) {
|
if (this.props.filterFunc && this.props.filter) {
|
||||||
let content = [key];
|
const filterOn = [key];
|
||||||
if (c.private && c.private.comment) {
|
if (c.private && c.private.comment) {
|
||||||
content.push(('' + c.private.comment).toLowerCase());
|
filterOn.push(('' + c.private.comment).toLowerCase());
|
||||||
}
|
}
|
||||||
if (c.public && c.public.fn) {
|
if (c.public && c.public.fn) {
|
||||||
content.push(('' + c.public.fn).toLowerCase());
|
filterOn.push(('' + c.public.fn).toLowerCase());
|
||||||
}
|
}
|
||||||
if (!this.props.filterFunc(this.props.filter, content)) {
|
if (!this.props.filterFunc(this.props.filter, filterOn)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,10 +78,10 @@ class ContactList extends React.Component {
|
|||||||
c.private.join(',') : (c.private ? c.private.comment : null);
|
c.private.join(',') : (c.private ? c.private.comment : null);
|
||||||
let preview;
|
let preview;
|
||||||
let deliveryStatus;
|
let deliveryStatus;
|
||||||
if (!this.props.showMode) {
|
if (!this.props.showMode && c.latestMessage) {
|
||||||
const msg = c.latestMessage();
|
const msg = c.latestMessage();
|
||||||
if (msg) {
|
if (msg) {
|
||||||
deliveryStatus = c.msgStatus(msg);
|
deliveryStatus = msg._status || c.msgStatus(msg, true);
|
||||||
preview = typeof msg.content == 'string' ?
|
preview = typeof msg.content == 'string' ?
|
||||||
msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) :
|
msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) :
|
||||||
Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH);
|
Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH);
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ export default class Contact extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
let title = this.props.title;
|
let title = this.props.title;
|
||||||
if (!title) {
|
if (!title) {
|
||||||
title = <i><FormattedMessage id="unnamed_topic" /></i>;
|
title = <i><FormattedMessage id="unnamed_topic" defaultMessage="Unnamed"
|
||||||
|
description="Title shown when the topic has no name" /></i>;
|
||||||
} else if (title.length > 30) {
|
} else if (title.length > 30) {
|
||||||
// FIXME: this is probably wrong for RTL languages.
|
// FIXME: this is probably wrong for RTL languages.
|
||||||
title = title.substring(0, 28) + '…';
|
title = title.substring(0, 28) + '…';
|
||||||
@@ -83,8 +84,9 @@ export default class Contact extends React.Component {
|
|||||||
{this.props.isChannel ? <img src="/img/channel.png" className="channel" alt="channel" /> : null}
|
{this.props.isChannel ? <img src="/img/channel.png" className="channel" alt="channel" /> : null}
|
||||||
<UnreadBadge count={this.props.unread} /><ContactBadges badges={icon_badges} />
|
<UnreadBadge count={this.props.unread} /><ContactBadges badges={icon_badges} />
|
||||||
</div>
|
</div>
|
||||||
<div className="contact-comment">{marker}{subtitle || '\u00A0'}</div>
|
{this.props.showMode ?
|
||||||
<span><ContactBadges badges={badges} /></span>
|
<span><ContactBadges badges={badges} /></span> :
|
||||||
|
<div className="contact-comment">{marker}{subtitle || '\u00A0'}</div>}
|
||||||
</div>
|
</div>
|
||||||
{this.props.showContextMenu ?
|
{this.props.showContextMenu ?
|
||||||
<span className="menuTrigger">
|
<span className="menuTrigger">
|
||||||
@@ -104,6 +106,11 @@ function draftyFormatter(style, data, values, key) {
|
|||||||
const attr = { key: key };
|
const attr = { key: key };
|
||||||
if (el) {
|
if (el) {
|
||||||
switch (style) {
|
switch (style) {
|
||||||
|
case 'BR':
|
||||||
|
// Replace new line with a space.
|
||||||
|
el = React.Fragment;
|
||||||
|
values = [' '];
|
||||||
|
break;
|
||||||
case 'HL':
|
case 'HL':
|
||||||
// Make highlight less prominent in preview.
|
// Make highlight less prominent in preview.
|
||||||
attr.className = 'highlight preview';
|
attr.className = 'highlight preview';
|
||||||
@@ -115,7 +122,7 @@ function draftyFormatter(style, data, values, key) {
|
|||||||
case 'IM':
|
case 'IM':
|
||||||
// Replace image with '[icon] Image'.
|
// Replace image with '[icon] Image'.
|
||||||
el = React.Fragment;
|
el = React.Fragment;
|
||||||
values = [<i className="material-icons">photo</i>, 'Picture'];
|
values = [<i key="im" className="material-icons">photo</i>, 'Picture'];
|
||||||
break;
|
break;
|
||||||
case 'BN':
|
case 'BN':
|
||||||
el = 'span';
|
el = 'span';
|
||||||
@@ -123,19 +130,19 @@ function draftyFormatter(style, data, values, key) {
|
|||||||
break;
|
break;
|
||||||
case 'FM':
|
case 'FM':
|
||||||
el = React.Fragment;
|
el = React.Fragment;
|
||||||
values = [<i className="material-icons">dashboard</i>, 'Form:'];
|
values = [<i key="fm" className="material-icons">dashboard</i>, 'Form: '].concat(values || []);
|
||||||
break;
|
break;
|
||||||
case 'RW':
|
case 'RW':
|
||||||
el = React.Fragment;
|
el = React.Fragment;
|
||||||
break;
|
break;
|
||||||
case 'EX':
|
case 'EX':
|
||||||
el = React.Fragment;
|
el = React.Fragment;
|
||||||
values = [<i className="material-icons">attachment</i>, 'Attachment'];
|
values = [<i key="ex" className="material-icons">attachment</i>, 'Attachment'];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (el == '_UNKN') {
|
if (el == '_UNKN') {
|
||||||
el = React.Fragment;
|
el = React.Fragment;
|
||||||
values = [<i className="material-icons">extension</i>];
|
values = [<i key="unkn" className="material-icons">extension</i>];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Clear for All',
|
defaultMessage: 'Clear for All',
|
||||||
description: 'Delete all message(s) for all members'
|
description: 'Delete all message(s) for all members'
|
||||||
},
|
},
|
||||||
'delete': {
|
delete: {
|
||||||
id: 'menu_item_delete',
|
id: 'menu_item_delete',
|
||||||
defaultMessage: 'Delete',
|
defaultMessage: 'Delete',
|
||||||
description: 'Delete selected messages'
|
description: 'Delete selected messages'
|
||||||
@@ -57,6 +57,11 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Are you sure you want to delete this conversation?',
|
defaultMessage: 'Are you sure you want to delete this conversation?',
|
||||||
description: 'Alert warning when deleting entire topic'
|
description: 'Alert warning when deleting entire topic'
|
||||||
},
|
},
|
||||||
|
delete_messages_warning: {
|
||||||
|
id: 'delete_messages_warning',
|
||||||
|
defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',
|
||||||
|
description: 'Alert dialog warning when hard-deleting all messages.'
|
||||||
|
},
|
||||||
unblock: {
|
unblock: {
|
||||||
id: 'menu_item_unblock',
|
id: 'menu_item_unblock',
|
||||||
defaultMessage: 'Unblock',
|
defaultMessage: 'Unblock',
|
||||||
@@ -82,7 +87,17 @@ const messages = defineMessages({
|
|||||||
id: 'menu_item_archive_topic',
|
id: 'menu_item_archive_topic',
|
||||||
defaultMessage: 'Archive',
|
defaultMessage: 'Archive',
|
||||||
description: 'Move topic from the list of active chats to archive'
|
description: 'Move topic from the list of active chats to archive'
|
||||||
}
|
},
|
||||||
|
edit_permissions: {
|
||||||
|
id: 'menu_item_edit_permissions',
|
||||||
|
defaultMessage: 'Edit permissions',
|
||||||
|
description: 'Menu item [Edit permissions]'
|
||||||
|
},
|
||||||
|
clear_messages_warning: {
|
||||||
|
id: 'clear_messages_warning',
|
||||||
|
defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',
|
||||||
|
description: 'Alert dialog warning when deleting all messages.'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class ContextMenu extends React.Component {
|
class ContextMenu extends React.Component {
|
||||||
@@ -107,8 +122,8 @@ class ContextMenu extends React.Component {
|
|||||||
title: formatMessage(messages.clear_messages),
|
title: formatMessage(messages.clear_messages),
|
||||||
handler: (params, errorHandler) => {
|
handler: (params, errorHandler) => {
|
||||||
return props.onShowAlert(
|
return props.onShowAlert(
|
||||||
formatMessage(messages.menu_item_clear_messages), // title
|
formatMessage(messages.clear_messages), // title
|
||||||
formatMessage({id: 'clear_messages_warning'}), // content
|
formatMessage(messages.clear_messages_warning), // content
|
||||||
(() => { this.deleteMessages(true, false, params, errorHandler); }), // onConfirm
|
(() => { this.deleteMessages(true, false, params, errorHandler); }), // onConfirm
|
||||||
null, // "OK"
|
null, // "OK"
|
||||||
true, // Show Reject button
|
true, // Show Reject button
|
||||||
@@ -121,8 +136,8 @@ class ContextMenu extends React.Component {
|
|||||||
title: formatMessage(messages.clear_for_all),
|
title: formatMessage(messages.clear_for_all),
|
||||||
handler: (params, errorHandler) => {
|
handler: (params, errorHandler) => {
|
||||||
return props.onShowAlert(
|
return props.onShowAlert(
|
||||||
formatMessage(messages.menu_item_clear_messages_for_all), // title
|
formatMessage(messages.clear_for_all), // title
|
||||||
formatMessage({id: 'delete_messages_warning'}), // content
|
formatMessage(message.delete_messages_warning), // content
|
||||||
(() => { return this.deleteMessages(true, true, params, errorHandler); }),
|
(() => { return this.deleteMessages(true, true, params, errorHandler); }),
|
||||||
null, // "OK"
|
null, // "OK"
|
||||||
true, // Show Reject button
|
true, // Show Reject button
|
||||||
@@ -171,7 +186,7 @@ class ContextMenu extends React.Component {
|
|||||||
title: formatMessage(messages.block),
|
title: formatMessage(messages.block),
|
||||||
handler: (params, errorHandler) => {
|
handler: (params, errorHandler) => {
|
||||||
return props.onShowAlert(
|
return props.onShowAlert(
|
||||||
formatMessage(messages.menu_item_block), // title
|
formatMessage(messages.block), // title
|
||||||
formatMessage(messages.topic_block_warning), // content
|
formatMessage(messages.topic_block_warning), // content
|
||||||
(() => {
|
(() => {
|
||||||
return this.topicPermissionSetter('-JP', params, errorHandler).then((ctrl) => {
|
return this.topicPermissionSetter('-JP', params, errorHandler).then((ctrl) => {
|
||||||
@@ -190,7 +205,7 @@ class ContextMenu extends React.Component {
|
|||||||
title: formatMessage(messages.topic_delete),
|
title: formatMessage(messages.topic_delete),
|
||||||
handler: (params, errorHandler) => {
|
handler: (params, errorHandler) => {
|
||||||
return props.onShowAlert(
|
return props.onShowAlert(
|
||||||
formatMessage(messages.menu_item_delete_topic), // title
|
formatMessage(messages.topic_delete), // title
|
||||||
formatMessage(messages.topic_delete_warning), // content
|
formatMessage(messages.topic_delete_warning), // content
|
||||||
(() => {
|
(() => {
|
||||||
const topic = this.props.tinode.getTopic(params.topicName);
|
const topic = this.props.tinode.getTopic(params.topicName);
|
||||||
@@ -229,7 +244,7 @@ class ContextMenu extends React.Component {
|
|||||||
// menu_item_edit_permissions is defined elsewhere.
|
// menu_item_edit_permissions is defined elsewhere.
|
||||||
'permissions': {
|
'permissions': {
|
||||||
id: 'permissions',
|
id: 'permissions',
|
||||||
title: formatMessage({id: 'menu_item_edit_permissions'}),
|
title: formatMessage(messages.edit_permissions),
|
||||||
handler: null
|
handler: null
|
||||||
},
|
},
|
||||||
'member_delete': {
|
'member_delete': {
|
||||||
|
|||||||
@@ -37,8 +37,10 @@ export default class DocPreview extends React.PureComponent {
|
|||||||
<div id="image-preview-container">
|
<div id="image-preview-container">
|
||||||
<div className="flex-column narrow">
|
<div className="flex-column narrow">
|
||||||
<i className="material-icons gray">{iconFromMime(this.props.content.type)}</i>
|
<i className="material-icons gray">{iconFromMime(this.props.content.type)}</i>
|
||||||
<div><b><FormattedMessage id="label_content_type" /></b> {this.props.content.type || 'application/octet-stream'}</div>
|
<div><b><FormattedMessage id="label_content_type" defaultMessage="Content type:"
|
||||||
<div><b><FormattedMessage id="label_size" /></b> {bytesToHumanSize(this.props.content.size)}</div>
|
description="Label for file content type (mime)" /></b> {this.props.content.type || 'application/octet-stream'}</div>
|
||||||
|
<div><b><FormattedMessage id="label_size" defaultMessage="Size:"
|
||||||
|
description="Label for file size" /></b> {bytesToHumanSize(this.props.content.size)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SendMessage
|
<SendMessage
|
||||||
|
|||||||
@@ -188,10 +188,10 @@ class GroupManager extends React.Component {
|
|||||||
showUnread={false}
|
showUnread={false}
|
||||||
onTopicSelected={this.handleContactSelected} />
|
onTopicSelected={this.handleContactSelected} />
|
||||||
<div id="group-manager-buttons" className="panel-form-row">
|
<div id="group-manager-buttons" className="panel-form-row">
|
||||||
<button className="blue" onClick={this.handleSubmit}>
|
<button className="primary" onClick={this.handleSubmit}>
|
||||||
<FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />
|
<FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />
|
||||||
</button>
|
</button>
|
||||||
<button className="white" onClick={this.handleCancel}>
|
<button className="secondary" onClick={this.handleCancel}>
|
||||||
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
|
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,15 +23,15 @@ export default class Invitation extends React.PureComponent {
|
|||||||
description="New chat invitation message: [Accept] [Ignore] [Block]." />
|
description="New chat invitation message: [Accept] [Ignore] [Block]." />
|
||||||
</div>
|
</div>
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
<button className="blue" onClick={event => { this.handleButtonAction(event, "accept"); }}>
|
<button className="primary" onClick={event => { this.handleButtonAction(event, "accept"); }}>
|
||||||
<FormattedMessage id="chat_invitation_accept"
|
<FormattedMessage id="chat_invitation_accept"
|
||||||
defaultMessage="Accept" description="Action [Accept] for chat invitation." />
|
defaultMessage="Accept" description="Action [Accept] for chat invitation." />
|
||||||
</button>
|
</button>
|
||||||
<button className="white" onClick={event => { this.handleButtonAction(event, "delete"); }}>
|
<button className="secondary" onClick={event => { this.handleButtonAction(event, "delete"); }}>
|
||||||
<FormattedMessage id="chat_invitation_ignore"
|
<FormattedMessage id="chat_invitation_ignore"
|
||||||
defaultMessage="Ignore" description="Action [Ignore] for chat invitation." />
|
defaultMessage="Ignore" description="Action [Ignore] for chat invitation." />
|
||||||
</button>
|
</button>
|
||||||
<button className="white" onClick={event => { this.handleButtonAction(event, "block"); }}>
|
<button className="secondary" onClick={event => { this.handleButtonAction(event, "block"); }}>
|
||||||
<FormattedMessage id="chat_invitation_block"
|
<FormattedMessage id="chat_invitation_block"
|
||||||
defaultMessage="Block" description="Action [Block] for chat invitation." />
|
defaultMessage="Block" description="Action [Block] for chat invitation." />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ class NewTopicById extends React.PureComponent {
|
|||||||
handleSubmit(e) {
|
handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.state.groupId) {
|
if (this.state.groupId) {
|
||||||
var name = this.state.groupId.trim();
|
const name = this.state.groupId.trim();
|
||||||
if (name.length > 3 && (name.substr(0, 3) == 'usr' || name.substr(0, 3) == 'grp')) {
|
const prefix = name.substr(0, 3);
|
||||||
|
if (name.length > 3 && ['usr', 'grp', 'chn'].includes(prefix)) {
|
||||||
this.props.onSubmit(name);
|
this.props.onSubmit(name);
|
||||||
} else {
|
} else {
|
||||||
this.props.onError(this.props.intl.formatMessage(messages.invalid_id), 'err');
|
this.props.onError(this.props.intl.formatMessage(messages.invalid_id), 'err');
|
||||||
@@ -56,7 +57,7 @@ class NewTopicById extends React.PureComponent {
|
|||||||
}</FormattedMessage>
|
}</FormattedMessage>
|
||||||
</div>
|
</div>
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button className="blue" onClick={this.handleSubmit}>
|
<button className="primary" onClick={this.handleSubmit}>
|
||||||
<FormattedMessage id="button_subscribe" defaultMessage="Subscribe"
|
<FormattedMessage id="button_subscribe" defaultMessage="Subscribe"
|
||||||
description="Button [Subscribe]" />
|
description="Button [Subscribe]" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default class NewTopicGroup extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var submitClasses = 'blue';
|
let submitClasses = 'primary';
|
||||||
if (this.props.disabled) {
|
if (this.props.disabled) {
|
||||||
submitClasses += ' disabled';
|
submitClasses += ' disabled';
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,8 @@ export default class NewTopicGroup extends React.PureComponent {
|
|||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<div className="panel-form-column">
|
<div className="panel-form-column">
|
||||||
<label className="small" htmlFor="new-topic-fn">
|
<label className="small" htmlFor="new-topic-fn">
|
||||||
<FormattedMessage id="label_topic_name" />
|
<FormattedMessage id="label_topic_name" defaultMessage="Name"
|
||||||
|
description="Label for editing topic name" />
|
||||||
</label>
|
</label>
|
||||||
<FormattedMessage id="topic_name_editing_placeholder" defaultMessage="Freeform name of the group"
|
<FormattedMessage id="topic_name_editing_placeholder" defaultMessage="Freeform name of the group"
|
||||||
description="Prompt for entering topic name">{
|
description="Prompt for entering topic name">{
|
||||||
@@ -76,9 +77,11 @@ export default class NewTopicGroup extends React.PureComponent {
|
|||||||
}</FormattedMessage>
|
}</FormattedMessage>
|
||||||
<br />
|
<br />
|
||||||
<label className="small" htmlFor="new-topic-priv">
|
<label className="small" htmlFor="new-topic-priv">
|
||||||
<FormattedMessage id="label_private" />
|
<FormattedMessage id="label_private" defaultMessage="Private comment"
|
||||||
|
description="Label for editing 'private'" />
|
||||||
</label>
|
</label>
|
||||||
<FormattedMessage id="private_editing_placeholder">{
|
<FormattedMessage id="private_editing_placeholder" defaultMessage="Visible to you only"
|
||||||
|
description="Placeholder for editing 'private'">{
|
||||||
(placeholder) => <input type="text" id="new-topic-priv" placeholder={placeholder}
|
(placeholder) => <input type="text" id="new-topic-priv" placeholder={placeholder}
|
||||||
value={this.state.private} onChange={this.handlePrivateChange} />
|
value={this.state.private} onChange={this.handlePrivateChange} />
|
||||||
}</FormattedMessage>
|
}</FormattedMessage>
|
||||||
@@ -93,7 +96,8 @@ export default class NewTopicGroup extends React.PureComponent {
|
|||||||
defaultMessage="This is a channel"
|
defaultMessage="This is a channel"
|
||||||
description="Checkbox label when creating a channel" /></label>
|
description="Checkbox label when creating a channel" /></label>
|
||||||
</div>
|
</div>
|
||||||
<FormattedMessage id="title_tag_manager">{
|
<FormattedMessage id="title_tag_manager" defaultMessage="Tags (user discovery)"
|
||||||
|
description="Section title for TagManager">{
|
||||||
(title) => <TagManager
|
(title) => <TagManager
|
||||||
tags={this.state.tags}
|
tags={this.state.tags}
|
||||||
activated={true}
|
activated={true}
|
||||||
|
|||||||
@@ -10,42 +10,42 @@ import { makeImageUrl } from '../lib/blob-helpers.js';
|
|||||||
|
|
||||||
// Translatable permission names.
|
// Translatable permission names.
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
'joiner': {
|
joiner: {
|
||||||
id: 'permission_join',
|
id: 'permission_join',
|
||||||
defaultMessage: "Join ({val})",
|
defaultMessage: "Join ({val})",
|
||||||
description: 'Name of J permission'
|
description: 'Name of J permission'
|
||||||
},
|
},
|
||||||
'reader': {
|
reader: {
|
||||||
id: 'permission_read',
|
id: 'permission_read',
|
||||||
defaultMessage: "Read ({val})",
|
defaultMessage: "Read ({val})",
|
||||||
description: 'Name of R permission'
|
description: 'Name of R permission'
|
||||||
},
|
},
|
||||||
'writer': {
|
writer: {
|
||||||
id: 'permission_write',
|
id: 'permission_write',
|
||||||
defaultMessage: "Write ({val})",
|
defaultMessage: "Write ({val})",
|
||||||
description: 'Name of W permission'
|
description: 'Name of W permission'
|
||||||
},
|
},
|
||||||
'preser': {
|
preser: {
|
||||||
id: 'permission_pres',
|
id: 'permission_pres',
|
||||||
defaultMessage: "Get notified ({val})",
|
defaultMessage: "Get notified ({val})",
|
||||||
description: 'Name of P permission'
|
description: 'Name of P permission'
|
||||||
},
|
},
|
||||||
'approver': {
|
approver: {
|
||||||
id: 'permission_admin',
|
id: 'permission_admin',
|
||||||
defaultMessage: "Approve ({val})",
|
defaultMessage: "Approve ({val})",
|
||||||
description: 'Name of A permission'
|
description: 'Name of A permission'
|
||||||
},
|
},
|
||||||
'sharer': {
|
sharer: {
|
||||||
id: 'permission_share',
|
id: 'permission_share',
|
||||||
defaultMessage: "Share ({val})",
|
defaultMessage: "Share ({val})",
|
||||||
description: 'Name of S permission'
|
description: 'Name of S permission'
|
||||||
},
|
},
|
||||||
'deleter': {
|
deleter: {
|
||||||
id: 'permission_delete',
|
id: 'permission_delete',
|
||||||
defaultMessage: "Delete ({val})",
|
defaultMessage: "Delete ({val})",
|
||||||
description: 'Name of D permission'
|
description: 'Name of D permission'
|
||||||
},
|
},
|
||||||
'owner': {
|
owner: {
|
||||||
id: 'permission_owner',
|
id: 'permission_owner',
|
||||||
defaultMessage: "Owner ({val})",
|
defaultMessage: "Owner ({val})",
|
||||||
description: 'Name of O permission'
|
description: 'Name of O permission'
|
||||||
@@ -151,10 +151,10 @@ class PermissionsEditor extends React.Component {
|
|||||||
<br />
|
<br />
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button className="outline" onClick={this.handleCancel}>
|
<button className="outline" onClick={this.handleCancel}>
|
||||||
<FormattedMessage id="button_cancel" />
|
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
|
||||||
</button>
|
</button>
|
||||||
<button className="blue" onClick={this.handleSubmit}>
|
<button className="primary" onClick={this.handleSubmit}>
|
||||||
<FormattedMessage id="button_ok" />
|
<FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import { shortDateFormat } from '../lib/strformat.js';
|
|||||||
import { deliveryMarker } from '../lib/utils.js';
|
import { deliveryMarker } from '../lib/utils.js';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
'sending': {
|
message_sending: {
|
||||||
'id': 'message_sending',
|
id: 'message_sending',
|
||||||
'defaultMessage': 'sending...',
|
defaultMessage: 'sending...',
|
||||||
'description': 'Message being sent, in place of time stamp'
|
description: 'Message being sent, in place of time stamp'
|
||||||
},
|
},
|
||||||
'failed': {
|
message_sending_failed: {
|
||||||
'id': 'message_sending_failed',
|
id: 'message_sending_failed',
|
||||||
'defaultMessage': 'failed',
|
defaultMessage: 'failed',
|
||||||
'description': 'Failed to send message, in place of time stamp'
|
description: 'Failed to send message, in place of time stamp'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -25,9 +25,9 @@ class ReceivedMarker extends React.PureComponent {
|
|||||||
const {formatMessage} = this.props.intl;
|
const {formatMessage} = this.props.intl;
|
||||||
let timestamp;
|
let timestamp;
|
||||||
if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {
|
if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {
|
||||||
timestamp = formatMessage(messages.sending);
|
timestamp = formatMessage(messages.message_sending);
|
||||||
} else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {
|
} else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {
|
||||||
timestamp = formatMessage(messages.failed);
|
timestamp = formatMessage(messages.message_sending_failed);
|
||||||
} else {
|
} else {
|
||||||
timestamp = shortDateFormat(this.props.timestamp, this.props.intl.locale);
|
timestamp = shortDateFormat(this.props.timestamp, this.props.intl.locale);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,27 +7,27 @@ import { KEYPRESS_DELAY } from '../config.js';
|
|||||||
import { filePasted } from '../lib/blob-helpers.js';
|
import { filePasted } from '../lib/blob-helpers.js';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
'messaging_disabled': {
|
messaging_disabled: {
|
||||||
id: 'messaging_disabled_prompt',
|
id: 'messaging_disabled_prompt',
|
||||||
defaultMessage: 'Messaging disabled',
|
defaultMessage: 'Messaging disabled',
|
||||||
description: 'Prompt in SendMessage in read-only topic'
|
description: 'Prompt in SendMessage in read-only topic'
|
||||||
},
|
},
|
||||||
'type_new_message': {
|
type_new_message: {
|
||||||
id: 'new_message_prompt',
|
id: 'new_message_prompt',
|
||||||
defaultMessage: 'New message',
|
defaultMessage: 'New message',
|
||||||
description: 'Prompt in send message field'
|
description: 'Prompt in send message field'
|
||||||
},
|
},
|
||||||
'add_image_caption': {
|
add_image_caption: {
|
||||||
id: 'image_caption_prompt',
|
id: 'image_caption_prompt',
|
||||||
defaultMessage: 'Image caption',
|
defaultMessage: 'Image caption',
|
||||||
description: 'Prompt in SendMessage for attached image'
|
description: 'Prompt in SendMessage for attached image'
|
||||||
},
|
},
|
||||||
'file_attachment_too_large': {
|
file_attachment_too_large: {
|
||||||
id: 'file_attachment_too_large',
|
id: 'file_attachment_too_large',
|
||||||
defaultMessage: 'The file size {size} exceeds the {limit} limit.',
|
defaultMessage: 'The file size {size} exceeds the {limit} limit.',
|
||||||
description: 'Error message when attachment is too large'
|
description: 'Error message when attachment is too large'
|
||||||
},
|
},
|
||||||
'cannot_initiate_upload': {
|
cannot_initiate_upload: {
|
||||||
id: 'cannot_initiate_file_upload',
|
id: 'cannot_initiate_file_upload',
|
||||||
defaultMessage: 'Cannot initiate file upload.',
|
defaultMessage: 'Cannot initiate file upload.',
|
||||||
description: 'Generic error messagewhen attachment fails'
|
description: 'Generic error messagewhen attachment fails'
|
||||||
|
|||||||
@@ -128,10 +128,10 @@ export default class TagManager extends React.Component {
|
|||||||
{this.props.onSubmit || this.props.onCancel ?
|
{this.props.onSubmit || this.props.onCancel ?
|
||||||
<div id="tag-manager-buttons" className="dialog-buttons panel-form-row">
|
<div id="tag-manager-buttons" className="dialog-buttons panel-form-row">
|
||||||
<button className="outline" onClick={this.handleCancel}>
|
<button className="outline" onClick={this.handleCancel}>
|
||||||
<FormattedMessage id="button_cancel" defautMessage="Cancel" description="Rejection button [Cancel]" />
|
<FormattedMessage id="button_cancel" defaultMessage="Cancel" description="Button [Cancel]" />
|
||||||
</button>
|
</button>
|
||||||
<button className="blue" onClick={this.handleSubmit}>
|
<button className="primary" onClick={this.handleSubmit}>
|
||||||
<FormattedMessage id="button_ok" defautMessage="OK" description="Confirmation button [OK]" />
|
<FormattedMessage id="button_ok" defaultMessage="OK" description="Button [OK]" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 473 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 499 KiB |
|
After Width: | Height: | Size: 592 KiB |
|
Before Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 144 KiB |
@@ -26,7 +26,7 @@ module.exports = (env, argv) => {
|
|||||||
publicPath: '/umd/'
|
publicPath: '/umd/'
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: (argv.mode === 'production'),
|
minimize: (mode === 'prod'),
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new TerserPlugin({
|
new TerserPlugin({
|
||||||
terserOptions: {
|
terserOptions: {
|
||||||
|
|||||||