Compare commits
23
Commits
7d1011400e
...
1a884d2546
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a884d2546 | ||
|
|
16e688d4c1 | ||
|
|
16bfaddf2c | ||
|
|
1085d1caf6 | ||
|
|
abf5b19758 | ||
|
|
44e2fc3a23 | ||
|
|
e505b0a607 | ||
|
|
8ca7f5cd3e | ||
|
|
a74ad9dd63 | ||
|
|
c70e5ebf7e | ||
|
|
679276c2e2 | ||
|
|
5faacc1f53 | ||
|
|
4818ee7a25 | ||
|
|
0fb5132d24 | ||
|
|
ed6be52228 | ||
|
|
1dc5ea1655 | ||
|
|
6e04638441 | ||
|
|
89e9b85551 | ||
|
|
5bb829a6bc | ||
|
|
92611680e4 | ||
|
|
78f31b7cf1 | ||
|
|
79a1bc98b2 | ||
|
|
0b05d480c7 |
@@ -1,3 +1,4 @@
|
|||||||
umd/* binary
|
umd/* binary
|
||||||
package-lock.json binary
|
package-lock.json binary
|
||||||
*.min.css binary
|
*.min.css binary
|
||||||
|
src/i18n.min/* binary
|
||||||
|
|||||||
+151
-24
@@ -4,12 +4,15 @@
|
|||||||
/* Size of text in message bubbles */
|
/* Size of text in message bubbles */
|
||||||
--message-text-size: 10pt;
|
--message-text-size: 10pt;
|
||||||
|
|
||||||
|
/* Height of caption panel */
|
||||||
|
--caption-panel-height: 4rem;
|
||||||
|
|
||||||
/* Core Palette */
|
/* Core Palette */
|
||||||
--clr-bright: light-dark(#fff, #181818);
|
--clr-bright: light-dark(#fff, #181818);
|
||||||
--clr-dark: light-dark(#000, #fff);
|
--clr-dark: light-dark(#000, #fff);
|
||||||
--clr-transparent: transparent;
|
--clr-transparent: transparent;
|
||||||
--clr-std-gray: #808080; /* Standard gray color */
|
--clr-std-gray: #808080; /* Standard gray color */
|
||||||
--clr-std-ltgray: light-dark(silver, #3f3f3f); /* Standard silver color */
|
--clr-std-ltgray: light-dark(#c0c0c0, #3f3f3f); /* Standard silver color */
|
||||||
--clr-std-teal: teal; /* Standard teal color */
|
--clr-std-teal: teal; /* Standard teal color */
|
||||||
--clr-std-grey: grey; /* Standard grey color */
|
--clr-std-grey: grey; /* Standard grey color */
|
||||||
--clr-std-orange: #ffa500; /* Standard orange color */
|
--clr-std-orange: #ffa500; /* Standard orange color */
|
||||||
@@ -21,6 +24,7 @@
|
|||||||
--clr-primary-accent-transparent-medium: light-dark(rgba(33, 150, 243, 0.3), rgba(20, 70, 120, 0.3)); /* for active call shadow */
|
--clr-primary-accent-transparent-medium: light-dark(rgba(33, 150, 243, 0.3), rgba(20, 70, 120, 0.3)); /* for active call shadow */
|
||||||
--clr-primary-action: light-dark(#1e89e6, #1571c1); /* blue 600, primary buttons */
|
--clr-primary-action: light-dark(#1e89e6, #1571c1); /* blue 600, primary buttons */
|
||||||
--clr-primary-action-hover: light-dark(#2962ff, #0037b8); /* hover for primary action */
|
--clr-primary-action-hover: light-dark(#2962ff, #0037b8); /* hover for primary action */
|
||||||
|
--clr-secondary-accent: light-dark(#0097a8, #006570); /* Checkmark icon, unread badge bg */
|
||||||
--clr-primary-light-blue: light-dark(#bcdefb, #78a0be); /* light-blue 100, side caption panel text/link */
|
--clr-primary-light-blue: light-dark(#bcdefb, #78a0be); /* light-blue 100, side caption panel text/link */
|
||||||
--clr-light-blue-accent: light-dark(#0398e2, #0379b5); /* light-blue accent, reply quote border, pinned active dot */
|
--clr-light-blue-accent: light-dark(#0398e2, #0379b5); /* light-blue accent, reply quote border, pinned active dot */
|
||||||
|
|
||||||
@@ -37,9 +41,10 @@
|
|||||||
--clr-background-light-gray: light-dark(#f0f0f0, #0f0f0f); /* HR background, action button hover */
|
--clr-background-light-gray: light-dark(#f0f0f0, #0f0f0f); /* HR background, action button hover */
|
||||||
--clr-background-extra-light-gray: light-dark(#eee, #222); /* Flat button faux bg, tabbar bg, chip bg */
|
--clr-background-extra-light-gray: light-dark(#eee, #222); /* Flat button faux bg, tabbar bg, chip bg */
|
||||||
--clr-background-blue-gray-50: light-dark(#edf0f2, #202c33); /* Contact hover, messages panel, send message panel */
|
--clr-background-blue-gray-50: light-dark(#edf0f2, #202c33); /* Contact hover, messages panel, send message panel */
|
||||||
|
--clr-background-blue-gray-75: light-dark(#e1e8eb, #182124); /* Contact pinned */
|
||||||
--clr-background-blue-gray-100: light-dark(#cfd8dc, #20272a); /* Contact selected, uploader bar */
|
--clr-background-blue-gray-100: light-dark(#cfd8dc, #20272a); /* Contact selected, uploader bar */
|
||||||
--clr-background-blue-gray-300: light-dark(#b0bec5, #354146); /* Topic/Info caption panel bg, some lettertiles */
|
--clr-background-blue-gray-300: light-dark(#b0bec5, #354146); /* Topic/Info caption panel bg, some lettertiles */
|
||||||
--clr-blue-wash-light: light-dark(#f0f0ff, #224); /* bot form button bg, badge bg (blue) */
|
--clr-blue-wash-light: light-dark(#f0f0ff, #22222d); /* bot form button bg */
|
||||||
--clr-blue-wash-medium: light-dark(#d8e8ff, #123550); /* Pinned active bg */
|
--clr-blue-wash-medium: light-dark(#d8e8ff, #123550); /* Pinned active bg */
|
||||||
--clr-primary-wash: light-dark(#e3f2fd, #072f45); /* Secondary button hover, avatar upload hover, bubble meta deleted bg */
|
--clr-primary-wash: light-dark(#e3f2fd, #072f45); /* Secondary button hover, avatar upload hover, bubble meta deleted bg */
|
||||||
--clr-indigo-50: light-dark(#e8eaf6, #1a2c44); /* Outline button hover */
|
--clr-indigo-50: light-dark(#e8eaf6, #1a2c44); /* Outline button hover */
|
||||||
@@ -47,17 +52,16 @@
|
|||||||
/* Semantic Colors: Danger, Warning, Success, Info */
|
/* Semantic Colors: Danger, Warning, Success, Info */
|
||||||
--clr-danger-text: light-dark(#d33131, #931f1f); /* Danger text, invalid label */
|
--clr-danger-text: light-dark(#d33131, #931f1f); /* Danger text, invalid label */
|
||||||
--clr-danger-text-hover: light-dark(#f44034, #cc1100); /* Hover for danger text */
|
--clr-danger-text-hover: light-dark(#f44034, #cc1100); /* Hover for danger text */
|
||||||
--clr-error-text-muted: light-dark(hsl(0, 50%, 60%), hsl(0, 50%, 40%)); /* For less critical errors like invalid in-place edit icon/text */
|
--clr-error-text-muted: light-dark(#cc6666, #993333); /* For less critical errors like invalid in-place edit icon/text */
|
||||||
--clr-error-bg-light: light-dark(#ffccd1, #ff6675); /* Info box error bg */
|
--clr-error-bg-light: light-dark(#ffccd1, #ff6675); /* Info box error bg */
|
||||||
--clr-error-border-light: light-dark(#ffbdc6, #ff334e); /* Info box error border */
|
--clr-error-border-light: light-dark(#ffbdc6, #ff334e); /* Info box error border */
|
||||||
--clr-error-text-dark: light-dark(#b51c1c, #6a1111); /* Info box error text, icon */
|
--clr-error-text-dark: light-dark(#b51c1c, #de2323); /* Info box error text, icon */
|
||||||
--clr-error-icon: light-dark(#ef5552, #d81b0e); /* Info box error icon specific */
|
--clr-error-icon: light-dark(#ef5552, #d81b0e); /* Info box error icon specific */
|
||||||
--clr-warning-accent: light-dark(#FFC107, #000e16); /* Amber, danger-color */
|
--clr-warning-accent: light-dark(#FFC107, #000e16); /* Amber, danger-color */
|
||||||
--clr-warning-bg-light: light-dark(#fff9c4, #00060c); /* Info box warning bg */
|
--clr-warning-bg-light: light-dark(#fff9c4, #00060c); /* Info box warning bg */
|
||||||
--clr-warning-border-light: light-dark(#fff59d, #000a2b); /* Info box warning border */
|
--clr-warning-border-light: light-dark(#fff59d, #000a2b); /* Info box warning border */
|
||||||
--clr-warning-text-dark: light-dark(#6d4c41, #251613); /* Info box warning text */
|
--clr-warning-text-dark: light-dark(#6d4c41, #a08356); /* Info box warning text */
|
||||||
--clr-warning-icon: light-dark(#fbc02d, #b48204); /* Info box warning icon specific */
|
--clr-warning-icon: light-dark(#fbc02d, #b48204); /* Info box warning icon specific */
|
||||||
--clr-success-text: light-dark(#388e3c, #1a3a1d); /* Green text */
|
|
||||||
--clr-info-bg-light: light-dark(#e1f5fe, #010a0e); /* Info box info bg */
|
--clr-info-bg-light: light-dark(#e1f5fe, #010a0e); /* Info box info bg */
|
||||||
--clr-info-border-light: light-dark(#b3e5fc, #001824); /* Info box info border */
|
--clr-info-border-light: light-dark(#b3e5fc, #001824); /* Info box info border */
|
||||||
--clr-info-text-dark: light-dark(#0288d1, #00151f); /* Info box info text, icon */
|
--clr-info-text-dark: light-dark(#0288d1, #00151f); /* Info box info text, icon */
|
||||||
@@ -71,7 +75,6 @@
|
|||||||
--clr-verified-accent: light-dark(#02a6f2, #027db6); /* Verified color */
|
--clr-verified-accent: light-dark(#02a6f2, #027db6); /* Verified color */
|
||||||
--clr-staff-accent: light-dark(#689f38, #598830); /* Staff color */
|
--clr-staff-accent: light-dark(#689f38, #598830); /* Staff color */
|
||||||
--clr-badge-inv: light-dark(#fffd, #cccd); /* Badge inverted text */
|
--clr-badge-inv: light-dark(#fffd, #cccd); /* Badge inverted text */
|
||||||
--clr-cyan-accent: light-dark(#0097a8, #006570); /* Checkmark icon, unread badge bg */
|
|
||||||
--clr-cyan-50: light-dark(#e0f7fa, #2f3e43); /* Dummy view bg */
|
--clr-cyan-50: light-dark(#e0f7fa, #2f3e43); /* Dummy view bg */
|
||||||
--clr-overlay-white-strong: light-dark(rgba(255, 255, 255, 0.9), rgba(0,0,0,0.90)); /* Image preview bg, video container bg */
|
--clr-overlay-white-strong: light-dark(rgba(255, 255, 255, 0.9), rgba(0,0,0,0.90)); /* Image preview bg, video container bg */
|
||||||
--clr-gray-medium-bg: light-dark(#777, #444); /* Image preview container bg (darker gray) */
|
--clr-gray-medium-bg: light-dark(#777, #444); /* Image preview container bg (darker gray) */
|
||||||
@@ -148,7 +151,7 @@
|
|||||||
--clr-badge-yellow-bg: light-dark(#ffe, #331); /* Note: #ffe is close to white, dark will be black */
|
--clr-badge-yellow-bg: light-dark(#ffe, #331); /* Note: #ffe is close to white, dark will be black */
|
||||||
--clr-badge-yellow-text: light-dark(#660, #660);
|
--clr-badge-yellow-text: light-dark(#660, #660);
|
||||||
--clr-badge-yellow-border: light-dark(#cc9, #552);
|
--clr-badge-yellow-border: light-dark(#cc9, #552);
|
||||||
--clr-badge-blue-bg: var(--clr-blue-wash-light);
|
--clr-badge-blue-bg: light-dark(#f0f0ff, #224);
|
||||||
--clr-badge-blue-text: light-dark(#339, #447);
|
--clr-badge-blue-text: light-dark(#339, #447);
|
||||||
--clr-badge-blue-border: light-dark(#aad, #336);
|
--clr-badge-blue-border: light-dark(#aad, #336);
|
||||||
--clr-badge-red-bg: light-dark(#fee, #522);
|
--clr-badge-red-bg: light-dark(#fee, #522);
|
||||||
@@ -256,7 +259,7 @@
|
|||||||
--clr-audio-icon: light-dark(rgba(102, 117, 117, 0.8), rgba(153, 138, 138, 0.8));
|
--clr-audio-icon: light-dark(rgba(102, 117, 117, 0.8), rgba(153, 138, 138, 0.8));
|
||||||
--clr-audio-icon-disabled: light-dark(rgba(153 153 153 / 75%), rgba(102 102 102 / 75%));
|
--clr-audio-icon-disabled: light-dark(rgba(153 153 153 / 75%), rgba(102 102 102 / 75%));
|
||||||
--clr-audio-timer-text: light-dark(rgba(102, 102, 102, 0.75), rgba(153, 153, 153, 0.75));
|
--clr-audio-timer-text: light-dark(rgba(102, 102, 102, 0.75), rgba(153, 153, 153, 0.75));
|
||||||
--clr-pinned-menu-bg: light-dark(#f8f8f8, #070707);
|
--clr-pinned-menu-bg: light-dark(#f8f8f8, #1a1a1a);
|
||||||
--clr-drag-n-drop-border: var(--clr-std-grey);
|
--clr-drag-n-drop-border: var(--clr-std-grey);
|
||||||
--clr-drag-n-drop-text: var(--clr-std-grey);
|
--clr-drag-n-drop-text: var(--clr-std-grey);
|
||||||
--clr-drag-n-drop-bg: light-dark(rgba(255,255,255,.95), rgba(0,0,0,.95));
|
--clr-drag-n-drop-bg: light-dark(rgba(255,255,255,.95), rgba(0,0,0,.95));
|
||||||
@@ -622,6 +625,7 @@ ul.menu > li.disabled {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.menuTrigger a {
|
.menuTrigger a {
|
||||||
color: var(--clr-text-secondary);
|
color: var(--clr-text-secondary);
|
||||||
@@ -723,7 +727,7 @@ div.trusted-badge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.green {
|
.green {
|
||||||
color: var(--clr-success-text); /* green 700 */
|
color: light-dark(#388e3c, #1a9a1d); /* green 700 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.white {
|
.white {
|
||||||
@@ -1091,7 +1095,7 @@ table.permission-editor td.checkbox {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex: none;
|
flex: none;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
height: 4rem;
|
height: var(--caption-panel-height);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
box-shadow: 0 1px 2px var(--clr-shadow-gray-strong);
|
box-shadow: 0 1px 2px var(--clr-shadow-gray-strong);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@@ -1319,6 +1323,10 @@ i.search {
|
|||||||
.contact-box > li.selected {
|
.contact-box > li.selected {
|
||||||
background-color: var(--clr-background-blue-gray-100); /* blue-gray 100 highlight */
|
background-color: var(--clr-background-blue-gray-100); /* blue-gray 100 highlight */
|
||||||
}
|
}
|
||||||
|
.contact-box > li.tpinned {
|
||||||
|
border-right: 1px solid var(--clr-secondary-accent);
|
||||||
|
background-color: var(--clr-background-blue-gray-75); /* blue-gray 75 highlight */
|
||||||
|
}
|
||||||
.contact-box > li.action {
|
.contact-box > li.action {
|
||||||
min-height: 2.5rem;
|
min-height: 2.5rem;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
@@ -1478,7 +1486,7 @@ img.channel {
|
|||||||
width: 1.5rem;
|
width: 1.5rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: var(--clr-cyan-accent);
|
color: var(--clr-secondary-accent);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -1561,6 +1569,7 @@ img.channel {
|
|||||||
height: 10.5rem;
|
height: 10.5rem;
|
||||||
font-size: 4.5rem;
|
font-size: 4.5rem;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-upload.read-only {
|
.avatar-upload.read-only {
|
||||||
@@ -1929,7 +1938,7 @@ img.channel {
|
|||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
height: 4rem;
|
height: var(--caption-panel-height);
|
||||||
padding: 1rem 0.75rem 0 0.75rem;
|
padding: 1rem 0.75rem 0 0.75rem;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
@@ -1973,7 +1982,7 @@ img.channel {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
background-color: #ccc;
|
background-color: var(--clr-std-ltgray);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -1992,22 +2001,44 @@ img.channel {
|
|||||||
background-color: var(--clr-overlay-white-strong);
|
background-color: var(--clr-overlay-white-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
#video-container.minimized {
|
||||||
|
left: auto;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
width: 18rem;
|
||||||
|
height: 18rem;
|
||||||
|
background-color: unset;
|
||||||
|
user-select: none;
|
||||||
|
cursor: grab;
|
||||||
|
box-shadow: 0.15rem 0.15rem 1.5rem var(--clr-shadow-dialog);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
column-gap: 1rem;
|
column-gap: 1rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
align-self: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
|
||||||
bottom: 2.5rem;
|
bottom: 2.5rem;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.incoming-call .controls {
|
#controls.minimized {
|
||||||
|
column-gap: 0.5rem;
|
||||||
|
bottom: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incoming-call #controls {
|
||||||
bottom: 1rem;
|
bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controls button {
|
#controls button {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
@@ -2015,6 +2046,53 @@ img.channel {
|
|||||||
box-shadow: 0 3px 6px 0 var(--clr-shadow-button);
|
box-shadow: 0 3px 6px 0 var(--clr-shadow-button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#controls.minimized button {
|
||||||
|
width: 2.75rem;
|
||||||
|
height: 2.75rem;
|
||||||
|
box-shadow: 0 2px 4px 0 var(--clr-shadow-button);
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls button .material-icons {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls.minimized button .material-icons {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.full-screen {
|
||||||
|
position: absolute;
|
||||||
|
top: 2rem;
|
||||||
|
left: 2rem;
|
||||||
|
z-index: 1;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: transparent;
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimized button.full-screen {
|
||||||
|
top: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.full-screen:hover:not([disabled]), button:active:not([disabled]) {
|
||||||
|
color: var(--clr-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.full-screen .material-icons {
|
||||||
|
font-size: x-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimized button.full-screen .material-icons {
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
|
||||||
#video-container-panel {
|
#video-container-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -2029,7 +2107,11 @@ img.channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.call-party:hover {
|
.call-party:hover {
|
||||||
border: 2px solid var(--clr-primary-accent);
|
border-color: var(--clr-primary-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimized .call-party {
|
||||||
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.call-party.peer {
|
.call-party.peer {
|
||||||
@@ -2041,6 +2123,13 @@ img.channel {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.minimized .call-party.peer {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.call-party.self {
|
.call-party.self {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.5rem;
|
right: 0.5rem;
|
||||||
@@ -2096,6 +2185,12 @@ video {
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.minimized .caller-card .avatar-box {
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
font-size: 3.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Badge with "You" for current user or peer's name. */
|
/* Badge with "You" for current user or peer's name. */
|
||||||
.call-party .caller-name {
|
.call-party .caller-name {
|
||||||
max-width: 12rem;
|
max-width: 12rem;
|
||||||
@@ -2103,7 +2198,7 @@ video {
|
|||||||
border-radius: 0.35rem;
|
border-radius: 0.35rem;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
text-shadow: 0 0 1px var(--clr-bright);
|
text-shadow: 0 0 1px var(--clr-bright);
|
||||||
background-color: rgba(255, 255, 255, 0.7); /* Specific rgba, could be a variable if reused */
|
background-color: var(--clr-background-off-white);
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -2132,6 +2227,12 @@ video {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.minimized .caller-name {
|
||||||
|
padding: 0.1rem 0.35rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
.incoming-call {
|
.incoming-call {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--clr-bright);
|
background-color: var(--clr-bright);
|
||||||
@@ -2300,6 +2401,7 @@ video {
|
|||||||
padding: 0.5rem 0.5rem 0.5rem 0.5rem;
|
padding: 0.5rem 0.5rem 0.5rem 0.5rem;
|
||||||
background-color: var(--clr-bubble-meta-date-bg);
|
background-color: var(--clr-bubble-meta-date-bg);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Left triangle placed bottom left flush. */
|
/* Left triangle placed bottom left flush. */
|
||||||
@@ -2646,7 +2748,7 @@ ul.tabbar li.active > a {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
background-color: var(--clr-cyan-accent);
|
background-color: var(--clr-secondary-accent);
|
||||||
color: var(--clr-bright);
|
color: var(--clr-bright);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 1.35rem;
|
width: 1.35rem;
|
||||||
@@ -2817,6 +2919,7 @@ ul.tabbar li.active > a {
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.material-icons.small {
|
.material-icons.small {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
@@ -3103,8 +3206,8 @@ ul.tabbar li.active > a {
|
|||||||
|
|
||||||
.cropper .cutout {
|
.cropper .cutout {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 17rem;
|
width: 17.8rem;
|
||||||
height: 17rem;
|
height: 17.8rem;
|
||||||
border: 1px dashed var(--clr-cropper-cutout-border);
|
border: 1px dashed var(--clr-cropper-cutout-border);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -3391,7 +3494,7 @@ ul.tabbar li.active > a {
|
|||||||
|
|
||||||
.pinned-menu .menuTrigger {
|
.pinned-menu .menuTrigger {
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3575,7 +3678,31 @@ div.toast.show {
|
|||||||
width: unset;
|
width: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#video-container.minimized {
|
||||||
|
flex-direction: row;
|
||||||
|
left: 0;
|
||||||
|
top: var(--caption-panel-height);
|
||||||
|
width: 100%;
|
||||||
|
height: 11rem;
|
||||||
|
border-radius: unset;
|
||||||
|
box-shadow: 0 0 0.5rem var(--clr-shadow-gray-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls.minimized {
|
||||||
|
flex-direction: column;
|
||||||
|
row-gap: 0.5rem;
|
||||||
|
bottom: auto;
|
||||||
|
column-gap: unset;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimized .call-party {
|
||||||
|
border-radius: unset;
|
||||||
|
box-shadow: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.call-party.self {
|
.call-party.self {
|
||||||
bottom: 8.5rem;
|
bottom: 8.5rem;
|
||||||
|
border-radius: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tinode-webapp",
|
"name": "tinode-webapp",
|
||||||
"description": "Tinode messenger for the web",
|
"description": "Tinode messenger for the web",
|
||||||
"version": "0.25.0",
|
"version": "0.25.0-beta1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/tinode/webapp.git"
|
"url": "git+https://github.com/tinode/webapp.git"
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"react": "^18.3.0",
|
"react": "^18.3.0",
|
||||||
"react-dom": "^18.3.0",
|
"react-dom": "^18.3.0",
|
||||||
"react-intl": "^7.0.0",
|
"react-intl": "^7.0.0",
|
||||||
"tinode-sdk": "^0.24.4",
|
"tinode-sdk": "^0.25.0-beta1",
|
||||||
"webm-duration-fix": "^1.0.4"
|
"webm-duration-fix": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "آخر ظهور",
|
"translation": "آخر ظهور",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "المظهر",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "حجم النص:",
|
"translation": "حجم النص:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "إخفاء حالة اتصالك ومؤشرات الكتابة.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "إرسال بـ {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "اضغط Enter لسطر جديد",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "إرسال بـ Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "اضغط Shift + Enter لسطر جديد",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "عام",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "لوحة المفاتيح:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "تثبيت",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "إلغاء التثبيت",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, zero {{count, number} مشترك} one {{count, number} مشترك} two {{count, number} مشتركان} few {{count, number} مشتركين} many {{count, number} مشتركًا} other {{count, number} مشترك}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "الأعضاء:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "المشتركون:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, zero {{count, number} عضو} one {{count, number} عضو} two {{count, number} عضوان} few {{count, number} أعضاء} many {{count, number} عضوًا} other {{count, number} عضو}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Zuletzt gesehen",
|
"translation": "Zuletzt gesehen",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Erscheinungsbild",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Textgröße:",
|
"translation": "Textgröße:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "Online-Status und Tipp-Indikatoren verbergen.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Senden mit {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Drücken Sie Enter für eine neue Zeile",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Senden mit Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Drücken Sie Shift + Enter für eine neue Zeile",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "Allgemein",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Tastatur:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Anheften",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Loslösen",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} Abonnent} other {{count, number} Abonnenten}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Mitglieder:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Abonnenten:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} Mitglied} other {{count, number} Mitglieder}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-8
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Last seen",
|
"translation": "Last seen",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Appearance",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Text size:",
|
"translation": "Text size:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1787,5 +1780,89 @@
|
|||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Send on {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Press Enter for new line",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Send on Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Press Shift + Enter for new line",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "General",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Keyboard:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Pin",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Unpin",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} subscriber} other {{count, number} subscribers}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Members:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Subscribers:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} member} other {{count, number} members}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Últ. vez",
|
"translation": "Últ. vez",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Apariencia",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Tamaño del texto:",
|
"translation": "Tamaño del texto:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "Ocultar tu estado en línea e indicadores de escritura.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Enviar con {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Presiona Enter para nueva línea",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Enviar con Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Presiona Shift + Enter para nueva línea",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "General",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Teclado:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Fijar",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Desfijar",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} suscriptor} other {{count, number} suscriptores}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Miembros:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Suscriptores:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} miembro} other {{count, number} miembros}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -438,6 +438,14 @@
|
|||||||
"defaultMessage": "Incognito mode:",
|
"defaultMessage": "Incognito mode:",
|
||||||
"description": "Label for incognito mode toggle"
|
"description": "Label for incognito mode toggle"
|
||||||
},
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings"
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members"
|
||||||
|
},
|
||||||
"label_message_sound": {
|
"label_message_sound": {
|
||||||
"defaultMessage": "Message sound:",
|
"defaultMessage": "Message sound:",
|
||||||
"description": "Label for message sounds toggle"
|
"description": "Label for message sounds toggle"
|
||||||
@@ -500,6 +508,10 @@
|
|||||||
"defaultMessage": "Size:",
|
"defaultMessage": "Size:",
|
||||||
"description": "Label for file size"
|
"description": "Label for file size"
|
||||||
},
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers"
|
||||||
|
},
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
"description": "Label adjusting text size"
|
"description": "Label adjusting text size"
|
||||||
@@ -541,7 +553,7 @@
|
|||||||
"description": "Label for current user permissions"
|
"description": "Label for current user permissions"
|
||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"leave_chat_warning": {
|
"leave_chat_warning": {
|
||||||
@@ -568,6 +580,10 @@
|
|||||||
"defaultMessage": "Login",
|
"defaultMessage": "Login",
|
||||||
"description": "Placeholer for username/login"
|
"description": "Placeholer for username/login"
|
||||||
},
|
},
|
||||||
|
"member_count": {
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members"
|
||||||
|
},
|
||||||
"menu_item_archive_topic": {
|
"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"
|
||||||
@@ -816,6 +832,10 @@
|
|||||||
"defaultMessage": "phone",
|
"defaultMessage": "phone",
|
||||||
"description": "Dative case of 'phone', i.e. 'phone' in 'by phone'"
|
"description": "Dative case of 'phone', i.e. 'phone' in 'by phone'"
|
||||||
},
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top."
|
||||||
|
},
|
||||||
"pin_message": {
|
"pin_message": {
|
||||||
"defaultMessage": "Pin",
|
"defaultMessage": "Pin",
|
||||||
"description": "Menu item [Pin] for pinning message to the top."
|
"description": "Menu item [Pin] for pinning message to the top."
|
||||||
@@ -880,6 +900,26 @@
|
|||||||
"defaultMessage": "Saved messages",
|
"defaultMessage": "Saved messages",
|
||||||
"description": "Name of self topic for UI"
|
"description": "Name of self topic for UI"
|
||||||
},
|
},
|
||||||
|
"send_command": {
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter"
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled"
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter"
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled"
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView."
|
||||||
|
},
|
||||||
"sidepanel_title_acc_notifications": {
|
"sidepanel_title_acc_notifications": {
|
||||||
"defaultMessage": "Notifications",
|
"defaultMessage": "Notifications",
|
||||||
"description": "Sidepanel title for AccNotificationsView."
|
"description": "Sidepanel title for AccNotificationsView."
|
||||||
@@ -892,10 +932,6 @@
|
|||||||
"defaultMessage": "Account Settings",
|
"defaultMessage": "Account Settings",
|
||||||
"description": "Sidepanel title for AccountSettingsView."
|
"description": "Sidepanel title for AccountSettingsView."
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView."
|
|
||||||
},
|
|
||||||
"sidepanel_title_archive": {
|
"sidepanel_title_archive": {
|
||||||
"defaultMessage": "Archived Chats",
|
"defaultMessage": "Archived Chats",
|
||||||
"description": "Sidepanel title for ContactsView-Archive."
|
"description": "Sidepanel title for ContactsView-Archive."
|
||||||
@@ -932,6 +968,10 @@
|
|||||||
"defaultMessage": "Stay logged in",
|
"defaultMessage": "Stay logged in",
|
||||||
"description": "Label for a checkbox"
|
"description": "Label for a checkbox"
|
||||||
},
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers"
|
||||||
|
},
|
||||||
"tabtitle_find_user": {
|
"tabtitle_find_user": {
|
||||||
"defaultMessage": "find",
|
"defaultMessage": "find",
|
||||||
"description": "Tab title Find"
|
"description": "Tab title Find"
|
||||||
@@ -1000,6 +1040,10 @@
|
|||||||
"defaultMessage": "Unnamed",
|
"defaultMessage": "Unnamed",
|
||||||
"description": "Title shown when the topic has no name"
|
"description": "Title shown when the topic has no name"
|
||||||
},
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat."
|
||||||
|
},
|
||||||
"unpin_message": {
|
"unpin_message": {
|
||||||
"defaultMessage": "Unpin",
|
"defaultMessage": "Unpin",
|
||||||
"description": "Menu item [Unpin] for un-pinning the message."
|
"description": "Menu item [Unpin] for un-pinning the message."
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Vu pour la dernière fois",
|
"translation": "Vu pour la dernière fois",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Apparence",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Taille du texte :",
|
"translation": "Taille du texte :",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "Masquer votre statut en ligne et les indicateurs de frappe.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Envoyer avec {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Appuyez sur Entrée pour une nouvelle ligne",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Envoyer avec Entrée",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Appuyez sur Shift + Entrée pour une nouvelle ligne",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "Général",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Clavier :",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Épingler",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Désépingler",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} abonné} other {{count, number} abonnés}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Membres :",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Abonnés :",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} membre} other {{count, number} membres}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-23
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Ultimo visto",
|
"translation": "Ultimo visto",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1739,53 +1739,130 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
|
"incognito_mode_description": {
|
||||||
|
"translation": "Nascondi il tuo stato online e gli indicatori di digitazione.",
|
||||||
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
|
"description": "Description for incognito mode toggle",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Invia con {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Premi Invio per una nuova riga",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Invia con Invio",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Premi Shift + Invio per una nuova riga",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "Generale",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Tastiera:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
"color_schema_dark": {
|
"color_schema_dark": {
|
||||||
"translation": "",
|
"translation": "Scuro",
|
||||||
"defaultMessage": "Dark",
|
"defaultMessage": "Dark",
|
||||||
"description": "Name of the color schema",
|
"description": "Name of the color schema",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"color_schema_light": {
|
"color_schema_light": {
|
||||||
"translation": "",
|
"translation": "Chiaro",
|
||||||
"defaultMessage": "Light",
|
"defaultMessage": "Light",
|
||||||
"description": "Name of the color schema",
|
"description": "Name of the color schema",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"color_schema_system": {
|
"color_schema_system": {
|
||||||
"translation": "",
|
"translation": "Predefinito del sistema",
|
||||||
"defaultMessage": "System default",
|
"defaultMessage": "System default",
|
||||||
"description": "Name of the color schema",
|
"description": "Name of the color schema",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"incognito_mode_description": {
|
|
||||||
"translation": "",
|
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
|
||||||
"description": "Description for incognito mode toggle",
|
|
||||||
"missing": true,
|
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"label_color_schema": {
|
"label_color_schema": {
|
||||||
"translation": "",
|
"translation": "Tema:",
|
||||||
"defaultMessage": "Theme:",
|
"defaultMessage": "Theme:",
|
||||||
"description": "Label for selecting color scheme (dark, light) in Settings",
|
"description": "Label for selecting color scheme (dark, light) in Settings",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "",
|
"translation": "Dimensione testo:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
"description": "Label adjusting text size",
|
"description": "Label adjusting text size",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
"pin_chat": {
|
||||||
"translation": "",
|
"translation": "Fissa",
|
||||||
"defaultMessage": "Appearance",
|
"defaultMessage": "Pin",
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Sblocca",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} iscritto} other {{count, number} iscritti}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Membri:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Iscritti:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} membro} other {{count, number} membri}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "마지막으로본",
|
"translation": "마지막으로본",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "모양",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "텍스트 크기:",
|
"translation": "텍스트 크기:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "온라인 상태와 입력 표시기를 숨깁니다.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "{key}로 전송",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "새 줄로 이동하려면 Enter를 누르세요",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Enter로 전송",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "새 줄로 이동하려면 Shift + Enter를 누르세요",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "일반",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "키보드:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "고정",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "고정 해제",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, other {구독자 {count, number}명}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "멤버:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "구독자:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, other {멤버 {count, number}명}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Văzut ultima dată",
|
"translation": "Văzut ultima dată",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Aspect",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Dimensiune text:",
|
"translation": "Dimensiune text:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "Ascunde statusul tău online și indicatorii de tastare.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Trimite cu {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Apasă Enter pentru linie nouă",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Trimite cu Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Apasă Shift + Enter pentru linie nouă",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "General",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Tastatură:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Fixează",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Anulează fixarea",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} abonat} few {{count, number} abonați} other {{count, number} de abonați}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Membri:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Abonați:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} membru} few {{count, number} membri} other {{count, number} de membri}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Был активен",
|
"translation": "Был активен",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Оформление",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Размер текста:",
|
"translation": "Размер текста:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "Скрывать онлайн статус и индикаторы набора текста.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Отправлять на {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Нажмать Enter для новой строки",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Отправлять на Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Нажимать Shift + Enter для новой строки",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "Основные",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Клавиатура:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Закрепить",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Открепить",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} подписчик} few {{count, number} подписчика} many {{count, number} подписчиков} other {{count, number} подписчиков}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Участники:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Подписчики:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} участник} few {{count, number} участника} many {{count, number} участников} other {{count, number} участников}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "ล่าสุดเมื่อ",
|
"translation": "ล่าสุดเมื่อ",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "ลักษณะ",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "ขนาดตัวอักษร:",
|
"translation": "ขนาดตัวอักษร:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "ซ่อนสถานะออนไลน์และตัวบ่งชี้การพิมพ์ของคุณ",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "ส่งด้วย {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "กด Enter เพื่อขึ้นบรรทัดใหม่",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "ส่งด้วย Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "กด Shift + Enter เพื่อขึ้นบรรทัดใหม่",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "ทั่วไป",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "แป้นพิมพ์:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "ปักหมุด",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "เลิกปักหมุด",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, other {{count, number} ผู้ติดตาม}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "สมาชิก:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "ผู้ติดตาม:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, other {{count, number} สมาชิก}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "Був активний",
|
"translation": "Був активний",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "Вигляд",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "Розмір тексту:",
|
"translation": "Розмір тексту:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "Приховати ваш статус онлайн та індикатори набору тексту.",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "Надіслати на {key}",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "Натисніть Enter для нового рядка",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "Надіслати на Enter",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "Натисніть Shift + Enter для нового рядка",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "Загальні",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "Клавіатура:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "Закріпити",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "Відкріпити",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} підписник} few {{count, number} підписники} many {{count, number} підписників} other {{count, number} підписників}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "Учасники:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "Підписники:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, one {{count, number} учасник} few {{count, number} учасники} many {{count, number} учасників} other {{count, number} учасників}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "最後上線",
|
"translation": "最後上線",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "外觀",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "文字大小:",
|
"translation": "文字大小:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "隱藏您的線上狀態和正在輸入指示器。",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "按 {key} 傳送",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "按 Enter 換行",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "按 Enter 傳送",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "按 Shift + Enter 換行",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "一般",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "鍵盤:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "置頂",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "取消置頂",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, other {{count, number} 位訂閱者}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "成員:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "訂閱者:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, other {{count, number} 位成員}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-10
@@ -615,7 +615,7 @@
|
|||||||
},
|
},
|
||||||
"last_seen_timestamp": {
|
"last_seen_timestamp": {
|
||||||
"translation": "最后可见",
|
"translation": "最后可见",
|
||||||
"defaultMessage": "Last seen",
|
"defaultMessage": "Last seen: {timestamp}",
|
||||||
"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",
|
||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
@@ -1767,13 +1767,6 @@
|
|||||||
"missing": false,
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"sidepanel_title_appearance": {
|
|
||||||
"translation": "外观",
|
|
||||||
"defaultMessage": "Appearance",
|
|
||||||
"description": "Sidepanel title for AccAppearanceView.",
|
|
||||||
"missing": false,
|
|
||||||
"obsolete": false
|
|
||||||
},
|
|
||||||
"label_text_size": {
|
"label_text_size": {
|
||||||
"translation": "文字大小:",
|
"translation": "文字大小:",
|
||||||
"defaultMessage": "Text size:",
|
"defaultMessage": "Text size:",
|
||||||
@@ -1782,10 +1775,94 @@
|
|||||||
"obsolete": false
|
"obsolete": false
|
||||||
},
|
},
|
||||||
"incognito_mode_description": {
|
"incognito_mode_description": {
|
||||||
"translation": "",
|
"translation": "隐藏您的在线状态和正在输入指示器。",
|
||||||
"defaultMessage": "Hide your online status and typing indicators.",
|
"defaultMessage": "Hide your online status and typing indicators.",
|
||||||
"description": "Description for incognito mode toggle",
|
"description": "Description for incognito mode toggle",
|
||||||
"missing": true,
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command": {
|
||||||
|
"translation": "按 {key} 发送",
|
||||||
|
"defaultMessage": "Send on {key}",
|
||||||
|
"description": "Config option to send message on hitting CTRL(or Cmd)-Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_command_explained": {
|
||||||
|
"translation": "按 Enter 换行",
|
||||||
|
"defaultMessage": "Press Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on CTRL-Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain": {
|
||||||
|
"translation": "按 Enter 发送",
|
||||||
|
"defaultMessage": "Send on Enter",
|
||||||
|
"description": "Config option to send message on hitting Enter",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"send_plain_explained": {
|
||||||
|
"translation": "按 Shift + Enter 换行",
|
||||||
|
"defaultMessage": "Press Shift + Enter for new line",
|
||||||
|
"description": "Explanation how to enter newline when [Send on Enter] is enabled",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"sidepanel_title_acc_general": {
|
||||||
|
"translation": "常规",
|
||||||
|
"defaultMessage": "General",
|
||||||
|
"description": "Sidepanel title for AccGeneralView.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_keyboard": {
|
||||||
|
"translation": "键盘:",
|
||||||
|
"defaultMessage": "Keyboard:",
|
||||||
|
"description": "Label for send on enter settings",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"pin_chat": {
|
||||||
|
"translation": "置顶",
|
||||||
|
"defaultMessage": "Pin",
|
||||||
|
"description": "Menu item [Pin] for pinning chat to the top.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"unpin_chat": {
|
||||||
|
"translation": "取消置顶",
|
||||||
|
"defaultMessage": "Unpin",
|
||||||
|
"description": "Menu item [Unpin] for un-pinning the chat.",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"subscriber_count": {
|
||||||
|
"translation": "{count, plural, other {{count, number} 位订阅者}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} subscriber} two {{count, number} subscribers} three {{count, number} subscribers} few {{count, number} subscribers} many {{count, number} subscribers} other {{count, number} subscribers}}",
|
||||||
|
"description": "Count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_member_count": {
|
||||||
|
"translation": "成员:",
|
||||||
|
"defaultMessage": "Members:",
|
||||||
|
"description": "Label for the count of group topic members",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"label_subscriber_count": {
|
||||||
|
"translation": "订阅者:",
|
||||||
|
"defaultMessage": "Subscribers:",
|
||||||
|
"description": "Label for the count of channel subscribers",
|
||||||
|
"missing": false,
|
||||||
|
"obsolete": false
|
||||||
|
},
|
||||||
|
"member_count": {
|
||||||
|
"translation": "{count, plural, other {{count, number} 位成员}}",
|
||||||
|
"defaultMessage": "{count, plural, one {{count, number} member} two {{count, number} members} three {{count, number} members} few {{count, number} members} many {{count, number} members} other {{count, number} members}}",
|
||||||
|
"description": "Count of group topic members",
|
||||||
|
"missing": false,
|
||||||
"obsolete": false
|
"obsolete": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,3 +108,18 @@ export function flagEmoji(countryCode) {
|
|||||||
.map(char => 0x1F1A5 + char.charCodeAt());
|
.map(char => 0x1F1A5 + char.charCodeAt());
|
||||||
return String.fromCodePoint(...codePoints);
|
return String.fromCodePoint(...codePoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Truncate a string to a specific length and add ellipsis if needed.
|
||||||
|
// Support both LTR and RTL languages.
|
||||||
|
export function truncateString(str, maxLength) {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
if (str.length <= maxLength) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect RTL characters in the string and truncate accordingly.
|
||||||
|
const isRTL = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/.test(str);
|
||||||
|
return isRTL ? '…' + str.slice(0, maxLength - 1) : str.slice(0, maxLength - 1) + '…';
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import {
|
|||||||
bytesToHumanSize,
|
bytesToHumanSize,
|
||||||
shortenFileName,
|
shortenFileName,
|
||||||
idToColorClass,
|
idToColorClass,
|
||||||
flagEmoji
|
flagEmoji,
|
||||||
|
truncateString
|
||||||
} from './strformat.js';
|
} from './strformat.js';
|
||||||
|
|
||||||
describe('shortDateFormat', () => {
|
describe('shortDateFormat', () => {
|
||||||
@@ -152,3 +153,37 @@ describe('flagEmoji', () => {
|
|||||||
expect(flagEmoji('jp')).toBe('🇯🇵');
|
expect(flagEmoji('jp')).toBe('🇯🇵');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('truncateString', () => {
|
||||||
|
test('should not truncate if not needed', () => {
|
||||||
|
expect(truncateString('short', 10)).toBe('short');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should truncate and add ellipsis for long strings', () => {
|
||||||
|
expect(truncateString('this is a long string', 10)).toBe('this is a…');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle RTL strings (Arabic)', () => {
|
||||||
|
expect(truncateString('هذا مثال لنص عربي طويل يحتاج.', 10)).toBe('…هذا مثال ');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle RTL strings (Hebrew)', () => {
|
||||||
|
expect(truncateString('זה דוגמה לטקסט עברי ארוך', 10)).toBe('…זה דוגמה ');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle RTL strings (Persian/Farsi)', () => {
|
||||||
|
expect(truncateString('این یک نمونه متن فارسی طولانی است', 10)).toBe('…این یک نم');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle RTL strings (Urdu)', () => {
|
||||||
|
expect(truncateString('یہ اردو زبان کا ایک طویل جملہ ہے', 10)).toBe('…یہ اردو ز');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle RTL strings (Arabic with mixed punctuation)', () => {
|
||||||
|
expect(truncateString('النص العربي الطويل! متحف الفن', 10)).toBe('…النص العر');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle RTL strings (Hebrew with English mixed)', () => {
|
||||||
|
expect(truncateString('טקסט עברי עם English מעורב', 10)).toBe('…טקסט עברי');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -248,7 +248,3 @@ export function cancelablePromise(promise) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clips string to the specified length.
|
|
||||||
export function clipStr(str, length) {
|
|
||||||
return str && str.substring(0, length);
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -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.25.0";
|
export const PACKAGE_VERSION = "0.25.0-beta1";
|
||||||
|
|||||||
@@ -2,17 +2,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
export default class AccAppearanceView extends React.PureComponent {
|
export default class AccGeneralView extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
colorSchema: props.colorSchema || 'light dark',
|
colorSchema: props.colorSchema || 'light dark',
|
||||||
textSize: (props.textSize * 10) || '100',
|
textSize: (props.textSize * 10) || '100',
|
||||||
|
sendOnEnter: props.sendOnEnter || 'plain',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.isMac = ((navigator.userAgentData && navigator.userAgentData.platform)
|
||||||
|
|| navigator.platform).toLowerCase().startsWith('mac');
|
||||||
this.handleColorSchemaSelected = this.handleColorSchemaSelected.bind(this);
|
this.handleColorSchemaSelected = this.handleColorSchemaSelected.bind(this);
|
||||||
this.handleTextSizeChanged = this.handleTextSizeChanged.bind(this);
|
this.handleTextSizeChanged = this.handleTextSizeChanged.bind(this);
|
||||||
|
this.handleSendOnEnterSelected = this.handleSendOnEnterSelected.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleColorSchemaSelected(e) {
|
handleColorSchemaSelected(e) {
|
||||||
@@ -27,6 +31,11 @@ export default class AccAppearanceView extends React.PureComponent {
|
|||||||
this.props.onTextSizeChanged((e.currentTarget.value / 10) | 0);
|
this.props.onTextSizeChanged((e.currentTarget.value / 10) | 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSendOnEnterSelected(e) {
|
||||||
|
this.setState({sendOnEnter: e.currentTarget.value});
|
||||||
|
this.props.onSendOnEnterChanged(e.currentTarget.value);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="settings-form" className="scrollable-panel">
|
<div id="settings-form" className="scrollable-panel">
|
||||||
@@ -40,7 +49,7 @@ export default class AccAppearanceView extends React.PureComponent {
|
|||||||
<ul className="quoted">
|
<ul className="quoted">
|
||||||
<li key="system">
|
<li key="system">
|
||||||
<input type="radio" id="system" name="color-scheme-select" value="light dark"
|
<input type="radio" id="system" name="color-scheme-select" value="light dark"
|
||||||
checked={this.state.colorSchema === 'light dark'}
|
checked={this.state.colorSchema == 'light dark'}
|
||||||
onChange={this.handleColorSchemaSelected} />
|
onChange={this.handleColorSchemaSelected} />
|
||||||
<label htmlFor="system">
|
<label htmlFor="system">
|
||||||
<FormattedMessage id="color_schema_system" defaultMessage="System default" description="Name of the color schema"/>
|
<FormattedMessage id="color_schema_system" defaultMessage="System default" description="Name of the color schema"/>
|
||||||
@@ -49,7 +58,7 @@ export default class AccAppearanceView extends React.PureComponent {
|
|||||||
</li>
|
</li>
|
||||||
<li key="light">
|
<li key="light">
|
||||||
<input type="radio" id="light" name="color-scheme-select" value="light"
|
<input type="radio" id="light" name="color-scheme-select" value="light"
|
||||||
checked={this.state.colorSchema === 'light'}
|
checked={this.state.colorSchema == 'light'}
|
||||||
onChange={this.handleColorSchemaSelected} />
|
onChange={this.handleColorSchemaSelected} />
|
||||||
<label htmlFor="light">
|
<label htmlFor="light">
|
||||||
<FormattedMessage id="color_schema_light" defaultMessage="Light" description="Name of the color schema"/>
|
<FormattedMessage id="color_schema_light" defaultMessage="Light" description="Name of the color schema"/>
|
||||||
@@ -58,7 +67,7 @@ export default class AccAppearanceView extends React.PureComponent {
|
|||||||
</li>
|
</li>
|
||||||
<li key="dark">
|
<li key="dark">
|
||||||
<input type="radio" id="dark" name="color-scheme-select" value="dark"
|
<input type="radio" id="dark" name="color-scheme-select" value="dark"
|
||||||
checked={this.state.colorSchema === 'dark'}
|
checked={this.state.colorSchema == 'dark'}
|
||||||
onChange={this.handleColorSchemaSelected} />
|
onChange={this.handleColorSchemaSelected} />
|
||||||
<label htmlFor="dark">
|
<label htmlFor="dark">
|
||||||
<FormattedMessage id="color_schema_dark" defaultMessage="Dark" description="Name of the color schema"/>
|
<FormattedMessage id="color_schema_dark" defaultMessage="Dark" description="Name of the color schema"/>
|
||||||
@@ -89,6 +98,48 @@ export default class AccAppearanceView extends React.PureComponent {
|
|||||||
</datalist>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="hr" />
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label className="small">
|
||||||
|
<FormattedMessage id="label_keyboard" defaultMessage="Keyboard:"
|
||||||
|
description="Label for send on enter settings" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<ul className="quoted">
|
||||||
|
<li key="plain">
|
||||||
|
<input type="radio" id="plain" name="send-select" value="plain"
|
||||||
|
checked={this.state.sendOnEnter == 'plain'}
|
||||||
|
onChange={this.handleSendOnEnterSelected} />
|
||||||
|
<label htmlFor="send_plain">
|
||||||
|
<FormattedMessage id="send_plain" defaultMessage="Send on Enter"
|
||||||
|
description="Config option to send message on hitting Enter"/>
|
||||||
|
</label>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<span className="small gray">
|
||||||
|
<FormattedMessage id="send_plain_explained" defaultMessage="Press Shift + Enter for new line"
|
||||||
|
description="Explanation how to enter newline when [Send on Enter] is enabled" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li key="command">
|
||||||
|
<input type="radio" id="command" name="send-select" value="command"
|
||||||
|
checked={this.state.sendOnEnter == 'command'}
|
||||||
|
onChange={this.handleSendOnEnterSelected} />
|
||||||
|
<label htmlFor="send_command">
|
||||||
|
<FormattedMessage id="send_command" defaultMessage="Send on {key}"
|
||||||
|
values={{key: this.isMac ? '⌘ + Enter' : 'Ctrl + Enter'}}
|
||||||
|
description="Config option to send message on hitting CTRL(or Cmd)-Enter"/>
|
||||||
|
</label>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<span className="small gray">
|
||||||
|
<FormattedMessage id="send_command_explained" defaultMessage="Press Enter for new line"
|
||||||
|
description="Explanation how to enter newline when [Send on CTRL-Enter] is enabled" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ class AccSecurityView extends React.Component {
|
|||||||
checked={this.props.incognitoMode} onChange={this.handleCheckboxClick} />
|
checked={this.props.incognitoMode} onChange={this.handleCheckboxClick} />
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<span className="small">
|
<span className="small gray">
|
||||||
<FormattedMessage id="incognito_mode_description" defaultMessage="Hide your online status and typing indicators."
|
<FormattedMessage id="incognito_mode_description" defaultMessage="Hide your online status and typing indicators."
|
||||||
description="Description for incognito mode toggle" />
|
description="Description for incognito mode toggle" />
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import { parsePhoneNumber } from 'libphonenumber-js/mobile';
|
|||||||
|
|
||||||
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
import AvatarUpload from '../widgets/avatar-upload.jsx';
|
||||||
import BadgeList from '../widgets/badge-list.jsx';
|
import BadgeList from '../widgets/badge-list.jsx';
|
||||||
|
|
||||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
|
||||||
import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
|
|
||||||
import CredentialEdit from '../widgets/credential-edit.jsx';
|
import CredentialEdit from '../widgets/credential-edit.jsx';
|
||||||
|
|
||||||
|
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
|
import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
|
||||||
|
|
||||||
function _clip(str, length) {
|
function _clip(str, length) {
|
||||||
return str && str.substring(0, length);
|
return truncateString(str, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AccountSettingsView extends React.Component {
|
export default class AccountSettingsView extends React.Component {
|
||||||
@@ -148,9 +149,9 @@ export default class AccountSettingsView extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<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.onNavigate('appearance');}}>
|
<a href="#" className="flat-button" onClick={e => {e.preventDefault(); this.props.onNavigate('acc_general');}}>
|
||||||
<i className="material-icons">tune</i> <FormattedMessage id="sidepanel_title_appearance"
|
<i className="material-icons">tune</i> <FormattedMessage id="sidepanel_title_acc_general"
|
||||||
defaultMessage="Appearance" description="Sidepanel title for AccAppearanceView." />
|
defaultMessage="General" description="Sidepanel title for AccGeneralView." />
|
||||||
</a>
|
</a>
|
||||||
<a href="#" className="flat-button" onClick={e => {e.preventDefault(); this.props.onNavigate('notif');}}>
|
<a href="#" className="flat-button" onClick={e => {e.preventDefault(); this.props.onNavigate('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"
|
||||||
|
|||||||
@@ -27,8 +27,12 @@ export default class ContactsView extends React.Component {
|
|||||||
const contacts = [];
|
const contacts = [];
|
||||||
let unreadThreads = 0;
|
let unreadThreads = 0;
|
||||||
let archivedCount = 0;
|
let archivedCount = 0;
|
||||||
|
|
||||||
|
const me = props.tinode.getMeTopic();
|
||||||
props.chatList.forEach(c => {
|
props.chatList.forEach(c => {
|
||||||
const blocked = c.acs && !c.acs.isJoiner();
|
const blocked = c.acs && !c.acs.isJoiner();
|
||||||
|
c.pinned = me.pinnedTopicRank(c.topic);
|
||||||
|
|
||||||
// Show only blocked contacts only when props.blocked == true.
|
// Show only blocked contacts only when props.blocked == true.
|
||||||
if (blocked && props.blocked) {
|
if (blocked && props.blocked) {
|
||||||
contacts.push(c);
|
contacts.push(c);
|
||||||
@@ -50,7 +54,8 @@ export default class ContactsView extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
contacts.sort((a, b) => {
|
contacts.sort((a, b) => {
|
||||||
return (b.touched || 0) - (a.touched || 0);
|
const pin = b.pinned - a.pinned;
|
||||||
|
return pin != 0 ? pin : (b.touched || 0) - (a.touched || 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (archivedCount > 0) {
|
if (archivedCount > 0) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import VisiblePassword from '../widgets/visible-password.jsx';
|
|||||||
import LocalStorageUtil from '../lib/local-storage.js';
|
import LocalStorageUtil from '../lib/local-storage.js';
|
||||||
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
import { theCard } from '../lib/utils.js';
|
import { theCard } from '../lib/utils.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
|
|
||||||
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE,
|
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE,
|
||||||
MAX_TITLE_LENGTH } from '../config.js';
|
MAX_TITLE_LENGTH } from '../config.js';
|
||||||
@@ -75,6 +76,10 @@ export default class CreateAccountView extends React.PureComponent {
|
|||||||
|
|
||||||
handleImageChanged(mime, img) {
|
handleImageChanged(mime, img) {
|
||||||
this.setState({newAvatar: img, newAvatarMime: mime});
|
this.setState({newAvatar: img, newAvatarMime: mime});
|
||||||
|
if (!img) {
|
||||||
|
// User cleared the image.
|
||||||
|
this.setState({imageUrl: null, uploadUrl: null});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleSaveToken() {
|
handleToggleSaveToken() {
|
||||||
@@ -87,7 +92,7 @@ export default class CreateAccountView extends React.PureComponent {
|
|||||||
this.props.onCreateAccount(
|
this.props.onCreateAccount(
|
||||||
this.state.login.trim(),
|
this.state.login.trim(),
|
||||||
this.state.password.trim(),
|
this.state.password.trim(),
|
||||||
theCard(this.state.fn.trim().substring(0, MAX_TITLE_LENGTH), this.state.uploadUrl),
|
theCard(truncateString(this.state.fn.trim(), MAX_TITLE_LENGTH), this.state.uploadUrl),
|
||||||
{
|
{
|
||||||
'meth': this.state.meth,
|
'meth': this.state.meth,
|
||||||
'val': this.state.meth == 'email' ?
|
'val': this.state.meth == 'email' ?
|
||||||
@@ -100,7 +105,7 @@ export default class CreateAccountView extends React.PureComponent {
|
|||||||
// AvatarCropView calls this method when the user has cropped the image.
|
// AvatarCropView calls this method when the user has cropped the image.
|
||||||
handleAvatarCropped(mime, blob, width, height) {
|
handleAvatarCropped(mime, blob, width, height) {
|
||||||
const url = blob ? URL.createObjectURL(blob) : null;
|
const url = blob ? URL.createObjectURL(blob) : null;
|
||||||
this.setState({avatar: url, newAvatar: null, newAvatarMime: null});
|
this.setState({newAvatar: null, newAvatarMime: null});
|
||||||
if (blob) {
|
if (blob) {
|
||||||
this.uploadAvatar(mime, blob, width, height);
|
this.uploadAvatar(mime, blob, width, height);
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-7
@@ -1,6 +1,6 @@
|
|||||||
// InfoView: panel with topic/user info.
|
// InfoView: panel with topic/user info.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
import { FormattedMessage, FormattedNumber, defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import { AccessMode, Tinode } from 'tinode-sdk';
|
import { AccessMode, Tinode } from 'tinode-sdk';
|
||||||
|
|
||||||
@@ -20,7 +20,8 @@ import { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH,
|
|||||||
NO_ACCESS_MODE, TOAST_DURATION } from '../config.js';
|
NO_ACCESS_MODE, TOAST_DURATION } from '../config.js';
|
||||||
|
|
||||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
import { theCard, clipStr } from '../lib/utils.js';
|
import { theCard } from '../lib/utils.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
info: {
|
info: {
|
||||||
@@ -133,6 +134,7 @@ class InfoView extends React.Component {
|
|||||||
previousSubsUpdated: undefined,
|
previousSubsUpdated: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.propsUpdated = this.propsUpdated.bind(this);
|
||||||
this.resetSubs = this.resetSubs.bind(this);
|
this.resetSubs = this.resetSubs.bind(this);
|
||||||
this.resetDesc = this.resetDesc.bind(this);
|
this.resetDesc = this.resetDesc.bind(this);
|
||||||
this.resetTags = this.resetTags.bind(this);
|
this.resetTags = this.resetTags.bind(this);
|
||||||
@@ -152,8 +154,7 @@ class InfoView extends React.Component {
|
|||||||
this.handleBackNavigate = this.handleBackNavigate.bind(this);
|
this.handleBackNavigate = this.handleBackNavigate.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to separately handle component mount.
|
propsUpdated(props) {
|
||||||
componentDidUpdate(props) {
|
|
||||||
const topic = this.props.tinode.getTopic(props.topic);
|
const topic = this.props.tinode.getTopic(props.topic);
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
return;
|
return;
|
||||||
@@ -175,6 +176,15 @@ class InfoView extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must provide this for lazy loaded components.
|
||||||
|
componentDidMount() {
|
||||||
|
this.propsUpdated(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(props) {
|
||||||
|
this.propsUpdated(props);
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const topic = this.props.tinode.getTopic(this.props.topic);
|
const topic = this.props.tinode.getTopic(this.props.topic);
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
@@ -231,13 +241,13 @@ class InfoView extends React.Component {
|
|||||||
|
|
||||||
fullName: isSelf ?
|
fullName: isSelf ?
|
||||||
props.intl.formatMessage(messages.self_topic_name) :
|
props.intl.formatMessage(messages.self_topic_name) :
|
||||||
clipStr(topic.public && topic.public.fn, MAX_TITLE_LENGTH),
|
truncateString(topic.public && topic.public.fn, MAX_TITLE_LENGTH),
|
||||||
description: isSelf ?
|
description: isSelf ?
|
||||||
props.intl.formatMessage(messages.self_topic_comment) :
|
props.intl.formatMessage(messages.self_topic_comment) :
|
||||||
clipStr(topic.public && topic.public.note, MAX_TOPIC_DESCRIPTION_LENGTH),
|
truncateString(topic.public && topic.public.note, MAX_TOPIC_DESCRIPTION_LENGTH),
|
||||||
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
||||||
trustedBadges: badges,
|
trustedBadges: badges,
|
||||||
private: clipStr(topic.private && topic.private.comment, MAX_TITLE_LENGTH),
|
private: truncateString(topic.private && topic.private.comment, MAX_TITLE_LENGTH),
|
||||||
archived: topic.isArchived(),
|
archived: topic.isArchived(),
|
||||||
address: topic.name,
|
address: topic.name,
|
||||||
groupTopic: topic.isGroupType(),
|
groupTopic: topic.isGroupType(),
|
||||||
@@ -645,6 +655,20 @@ class InfoView extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
{this.state.groupTopic && topic.subcnt > 0 ?
|
||||||
|
<div className="group"><label className="small">
|
||||||
|
{this.state.channel ?
|
||||||
|
<FormattedMessage id="label_subscriber_count"
|
||||||
|
defaultMessage="Subscribers:"
|
||||||
|
description="Label for the count of channel subscribers" />
|
||||||
|
:
|
||||||
|
<FormattedMessage id="label_member_count"
|
||||||
|
defaultMessage="Members:"
|
||||||
|
description="Label for the count of group topic members" />
|
||||||
|
}
|
||||||
|
</label> <FormattedNumber value={topic.subcnt}/></div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
{this.state.description ?
|
{this.state.description ?
|
||||||
<div className="group">
|
<div className="group">
|
||||||
<label className="small">
|
<label className="small">
|
||||||
|
|||||||
+63
-26
@@ -44,7 +44,7 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
last_seen: {
|
last_seen: {
|
||||||
id: 'last_seen_timestamp',
|
id: 'last_seen_timestamp',
|
||||||
defaultMessage: 'Last seen',
|
defaultMessage: 'Last seen: {timestamp}',
|
||||||
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: {
|
||||||
@@ -82,6 +82,28 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Saved messages',
|
defaultMessage: 'Saved messages',
|
||||||
description: 'Name of self topic for UI'
|
description: 'Name of self topic for UI'
|
||||||
},
|
},
|
||||||
|
subscriber_count: {
|
||||||
|
id: 'subscriber_count',
|
||||||
|
defaultMessage: '{count, plural, ' +
|
||||||
|
'one {{count, number} subscriber} ' +
|
||||||
|
'two {{count, number} subscribers} ' +
|
||||||
|
'three {{count, number} subscribers} ' +
|
||||||
|
'few {{count, number} subscribers} ' +
|
||||||
|
'many {{count, number} subscribers} ' +
|
||||||
|
'other {{count, number} subscribers}}',
|
||||||
|
description: 'Count of channel subscribers'
|
||||||
|
},
|
||||||
|
member_count: {
|
||||||
|
id: 'member_count',
|
||||||
|
defaultMessage: '{count, plural, ' +
|
||||||
|
'one {{count, number} member} ' +
|
||||||
|
'two {{count, number} members} ' +
|
||||||
|
'three {{count, number} members} ' +
|
||||||
|
'few {{count, number} members} ' +
|
||||||
|
'many {{count, number} members} ' +
|
||||||
|
'other {{count, number} members}}',
|
||||||
|
description: 'Count of group topic members'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Checks if the access permissions are granted but not yet accepted.
|
// Checks if the access permissions are granted but not yet accepted.
|
||||||
@@ -135,6 +157,7 @@ class MessagesView extends React.Component {
|
|||||||
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
||||||
this.handleExpandMedia = this.handleExpandMedia.bind(this);
|
this.handleExpandMedia = this.handleExpandMedia.bind(this);
|
||||||
this.handleClosePreview = this.handleClosePreview.bind(this);
|
this.handleClosePreview = this.handleClosePreview.bind(this);
|
||||||
|
this.handleCallPanelToggle = this.handleCallPanelToggle.bind(this);
|
||||||
this.handleFormResponse = this.handleFormResponse.bind(this);
|
this.handleFormResponse = this.handleFormResponse.bind(this);
|
||||||
this.handleContextClick = this.handleContextClick.bind(this);
|
this.handleContextClick = this.handleContextClick.bind(this);
|
||||||
this.handleShowMessageContextMenu = this.handleShowMessageContextMenu.bind(this);
|
this.handleShowMessageContextMenu = this.handleShowMessageContextMenu.bind(this);
|
||||||
@@ -322,6 +345,7 @@ class MessagesView extends React.Component {
|
|||||||
videoPreview: null,
|
videoPreview: null,
|
||||||
videoPostview: null,
|
videoPostview: null,
|
||||||
rtcPanel: null,
|
rtcPanel: null,
|
||||||
|
minimizedCallPanel: false,
|
||||||
typingIndicator: false,
|
typingIndicator: false,
|
||||||
scrollPosition: 0,
|
scrollPosition: 0,
|
||||||
fetchingMessages: false,
|
fetchingMessages: false,
|
||||||
@@ -348,6 +372,7 @@ class MessagesView extends React.Component {
|
|||||||
videoPreview: null,
|
videoPreview: null,
|
||||||
videoPostview: null,
|
videoPostview: null,
|
||||||
rtcPanel: null,
|
rtcPanel: null,
|
||||||
|
minimizedCallPanel: false,
|
||||||
typingIndicator: false,
|
typingIndicator: false,
|
||||||
scrollPosition: 0,
|
scrollPosition: 0,
|
||||||
fetchingMessages: false,
|
fetchingMessages: false,
|
||||||
@@ -1084,6 +1109,10 @@ class MessagesView extends React.Component {
|
|||||||
this.props.onCallHangup(topic, seq);
|
this.props.onCallHangup(topic, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCallPanelToggle() {
|
||||||
|
this.setState({minimizedCallPanel: !this.state.minimizedCallPanel});
|
||||||
|
}
|
||||||
|
|
||||||
// sendImageAttachment sends the image bits as Drafty message.
|
// sendImageAttachment sends the image bits as Drafty message.
|
||||||
sendImageAttachment(caption, blob) {
|
sendImageAttachment(caption, blob) {
|
||||||
const mime = this.state.imagePreview.mime;
|
const mime = this.state.imagePreview.mime;
|
||||||
@@ -1457,6 +1486,28 @@ class MessagesView extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const {formatMessage} = this.props.intl;
|
const {formatMessage} = this.props.intl;
|
||||||
|
|
||||||
|
// P2P call is an overlay, not a separate component because it cannot be
|
||||||
|
// re-mounted between full screen/minimized.
|
||||||
|
const overlay = this.state.rtcPanel ? (
|
||||||
|
<CallPanel
|
||||||
|
topic={this.state.topic}
|
||||||
|
seq={this.props.callSeq}
|
||||||
|
callState={this.props.callState}
|
||||||
|
callAudioOnly={this.props.callAudioOnly}
|
||||||
|
tinode={this.props.tinode}
|
||||||
|
title={this.state.title}
|
||||||
|
avatar={this.state.avatar || true}
|
||||||
|
minimized={this.state.minimizedCallPanel}
|
||||||
|
|
||||||
|
onError={this.props.onError}
|
||||||
|
onHangup={this.handleCallHangup}
|
||||||
|
onToggleMinimize={this.handleCallPanelToggle}
|
||||||
|
onInvite={this.props.onCallInvite}
|
||||||
|
onSendOffer={this.props.onCallSendOffer}
|
||||||
|
onIceCandidate={this.props.onCallIceCandidate}
|
||||||
|
onSendAnswer={this.props.onCallSendAnswer} />
|
||||||
|
) : null;
|
||||||
|
|
||||||
let component;
|
let component;
|
||||||
if (!this.state.topic) {
|
if (!this.state.topic) {
|
||||||
component = (
|
component = (
|
||||||
@@ -1516,28 +1567,9 @@ class MessagesView extends React.Component {
|
|||||||
onClose={this.handleClosePreview}
|
onClose={this.handleClosePreview}
|
||||||
onSendMessage={this.sendFileAttachment} />
|
onSendMessage={this.sendFileAttachment} />
|
||||||
);
|
);
|
||||||
} else if (this.state.rtcPanel) {
|
|
||||||
// P2P call.
|
|
||||||
component2 = (
|
|
||||||
<CallPanel
|
|
||||||
topic={this.state.topic}
|
|
||||||
seq={this.props.callSeq}
|
|
||||||
callState={this.props.callState}
|
|
||||||
callAudioOnly={this.props.callAudioOnly}
|
|
||||||
tinode={this.props.tinode}
|
|
||||||
title={this.state.title}
|
|
||||||
avatar={this.state.avatar || true}
|
|
||||||
|
|
||||||
onError={this.props.onError}
|
|
||||||
onHangup={this.handleCallHangup}
|
|
||||||
onInvite={this.props.onCallInvite}
|
|
||||||
onSendOffer={this.props.onCallSendOffer}
|
|
||||||
onIceCandidate={this.props.onCallIceCandidate}
|
|
||||||
onSendAnswer={this.props.onCallSendAnswer} />
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||||
const isChannel = topic.isChannelType();
|
const isChannel = topic.isChannelType() || topic.chan;
|
||||||
const groupTopic = topic.isGroupType() && !isChannel;
|
const groupTopic = topic.isGroupType() && !isChannel;
|
||||||
const icon_badges = [];
|
const icon_badges = [];
|
||||||
if (topic.trusted) {
|
if (topic.trusted) {
|
||||||
@@ -1652,17 +1684,21 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
let lastSeen = null;
|
let lastSeen = null;
|
||||||
if (isChannel) {
|
if (isChannel) {
|
||||||
lastSeen = formatMessage(messages.channel);
|
lastSeen = formatMessage(messages.subscriber_count, {count: topic.subcnt});
|
||||||
} else {
|
} else {
|
||||||
const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);
|
const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);
|
||||||
if (cont && Tinode.isP2PTopicName(cont.topic)) {
|
if (cont) {
|
||||||
|
if (Tinode.isP2PTopicName(cont.topic)) {
|
||||||
if (cont.online) {
|
if (cont.online) {
|
||||||
lastSeen = formatMessage(messages.online_now);
|
lastSeen = formatMessage(messages.online_now);
|
||||||
} else if (cont.seen) {
|
} else if (cont.seen) {
|
||||||
lastSeen = formatMessage(messages.last_seen) + ": " +
|
lastSeen = formatMessage(messages.last_seen,
|
||||||
shortDateFormat(cont.seen.when, this.props.intl.locale);
|
{timestamp: shortDateFormat(cont.seen.when, this.props.intl.locale)});
|
||||||
// TODO: also handle user agent in c.seen.ua
|
// TODO: also handle user agent in c.seen.ua
|
||||||
}
|
}
|
||||||
|
} else if (Tinode.isGroupTopicName(cont.topic)) {
|
||||||
|
lastSeen = formatMessage(messages.member_count, {count: topic.subcnt});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const avatar = this.state.avatar || true;
|
const avatar = this.state.avatar || true;
|
||||||
@@ -1712,6 +1748,7 @@ class MessagesView extends React.Component {
|
|||||||
disabled={!this.state.isWriter || this.state.deleted}
|
disabled={!this.state.isWriter || this.state.deleted}
|
||||||
reply={this.state.reply}
|
reply={this.state.reply}
|
||||||
initMessage={this.state.contentToEdit}
|
initMessage={this.state.contentToEdit}
|
||||||
|
sendOnEnter={this.props.sendOnEnter}
|
||||||
onKeyPress={this.sendKeyPress}
|
onKeyPress={this.sendKeyPress}
|
||||||
onRecordingProgress={this.sendKeyPress}
|
onRecordingProgress={this.sendKeyPress}
|
||||||
onSendMessage={this.sendMessage}
|
onSendMessage={this.sendMessage}
|
||||||
@@ -1802,7 +1839,7 @@ class MessagesView extends React.Component {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
component = <div id="topic-view" ref={this.mountDnDEvents}>{component2}</div>
|
component = <div id="topic-view" ref={this.mountDnDEvents}>{component2}{overlay}</div>
|
||||||
}
|
}
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import TopicCommonView from './topic-common-view.jsx';
|
|||||||
const AccountSettingsView = React.lazy(_ => import('./account-settings-view.jsx'));
|
const AccountSettingsView = React.lazy(_ => import('./account-settings-view.jsx'));
|
||||||
import ContactsView from './contacts-view.jsx';
|
import ContactsView from './contacts-view.jsx';
|
||||||
const CreateAccountView = React.lazy(_ => import('./create-account-view.jsx'));
|
const CreateAccountView = React.lazy(_ => import('./create-account-view.jsx'));
|
||||||
import AccAppearanceView from './acc-appearance-view.jsx';
|
import AccGeneralView from './acc-general-view.jsx';
|
||||||
import AccNotificationsView from './acc-notifications-view.jsx';
|
import AccNotificationsView from './acc-notifications-view.jsx';
|
||||||
const AccSecurityView = React.lazy(_ => import('./acc-security-view.jsx'));
|
const AccSecurityView = React.lazy(_ => import('./acc-security-view.jsx'));
|
||||||
import AccSupportView from './acc-support-view.jsx';
|
import AccSupportView from './acc-support-view.jsx';
|
||||||
@@ -61,10 +61,10 @@ const messages = defineMessages({
|
|||||||
description: 'Sidepanel title for AccNotificationsView.',
|
description: 'Sidepanel title for AccNotificationsView.',
|
||||||
defaultMessage: 'Notifications'
|
defaultMessage: 'Notifications'
|
||||||
},
|
},
|
||||||
appearance: {
|
acc_general: {
|
||||||
id: 'sidepanel_title_appearance',
|
id: 'sidepanel_title_acc_general',
|
||||||
description: 'Sidepanel title for AccAppearanceView.',
|
description: 'Sidepanel title for AccGeneralView.',
|
||||||
defaultMessage: 'Appearance'
|
defaultMessage: 'General'
|
||||||
},
|
},
|
||||||
support: {
|
support: {
|
||||||
id: 'sidepanel_title_acc_support',
|
id: 'sidepanel_title_acc_support',
|
||||||
@@ -206,12 +206,14 @@ class SidepanelView extends React.PureComponent {
|
|||||||
onUpdateTagsRequest={this.props.onUpdateAccountTags}
|
onUpdateTagsRequest={this.props.onUpdateAccountTags}
|
||||||
onError={this.props.onError} /> :
|
onError={this.props.onError} /> :
|
||||||
|
|
||||||
view === 'appearance' ?
|
view === 'acc_general' ?
|
||||||
<AccAppearanceView
|
<AccGeneralView
|
||||||
colorSchema={this.props.colorSchema}
|
colorSchema={this.props.colorSchema}
|
||||||
textSize={this.props.textSize}
|
textSize={this.props.textSize}
|
||||||
|
sendOnEnter={this.props.sendOnEnter}
|
||||||
onChangeColorSchema={this.props.onChangeColorSchema}
|
onChangeColorSchema={this.props.onChangeColorSchema}
|
||||||
onTextSizeChanged={this.props.onTextSizeChanged} /> :
|
onTextSizeChanged={this.props.onTextSizeChanged}
|
||||||
|
onSendOnEnterChanged={this.props.onSendOnEnterChanged} /> :
|
||||||
|
|
||||||
view === 'notif' ?
|
view === 'notif' ?
|
||||||
<AccNotificationsView
|
<AccNotificationsView
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handleToggleIncognitoMode = this.handleToggleIncognitoMode.bind(this);
|
this.handleToggleIncognitoMode = this.handleToggleIncognitoMode.bind(this);
|
||||||
this.handleChangeColorSchema = this.handleChangeColorSchema.bind(this);
|
this.handleChangeColorSchema = this.handleChangeColorSchema.bind(this);
|
||||||
this.handleChangeTextSize = this.handleChangeTextSize.bind(this);
|
this.handleChangeTextSize = this.handleChangeTextSize.bind(this);
|
||||||
|
this.handleSendOnEnter = this.handleSendOnEnter.bind(this);
|
||||||
this.handleSettings = this.handleSettings.bind(this);
|
this.handleSettings = this.handleSettings.bind(this);
|
||||||
this.handleGlobalSettings = this.handleGlobalSettings.bind(this);
|
this.handleGlobalSettings = this.handleGlobalSettings.bind(this);
|
||||||
this.handleShowArchive = this.handleShowArchive.bind(this);
|
this.handleShowArchive = this.handleShowArchive.bind(this);
|
||||||
@@ -217,6 +218,7 @@ class TinodeWeb extends React.Component {
|
|||||||
incognitoMode: false,
|
incognitoMode: false,
|
||||||
colorSchema: settings.colorSchema || DEFAULT_COLOR_SCHEME, // default: system default
|
colorSchema: settings.colorSchema || DEFAULT_COLOR_SCHEME, // default: system default
|
||||||
textSize: settings.textSize || DEFAULT_TEXT_SIZE, // default size: 10pt
|
textSize: settings.textSize || DEFAULT_TEXT_SIZE, // default size: 10pt
|
||||||
|
sendOnEnter: settings.sendOnEnter || 'plain', // default: 'plain' (just Enter)
|
||||||
// Persistent request to enable alerts.
|
// Persistent request to enable alerts.
|
||||||
desktopAlerts: persist && !!settings.desktopAlerts,
|
desktopAlerts: persist && !!settings.desktopAlerts,
|
||||||
// Enable / disable checkbox.
|
// Enable / disable checkbox.
|
||||||
@@ -524,7 +526,7 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
if (hash.path && hash.path.length > 0) {
|
if (hash.path && hash.path.length > 0) {
|
||||||
// Left-side panel selector.
|
// Left-side panel selector.
|
||||||
if (['register','settings','edit','notif','appearance','security','support','general','crop',
|
if (['register','settings','edit','notif','acc_general','security','support','general','crop',
|
||||||
'cred','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {
|
'cred','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {
|
||||||
newState.sidePanelSelected = hash.path[0];
|
newState.sidePanelSelected = hash.path[0];
|
||||||
} else {
|
} else {
|
||||||
@@ -841,13 +843,17 @@ class TinodeWeb extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tnMeMetaDesc(desc) {
|
tnMeMetaDesc(desc) {
|
||||||
if (desc) {
|
if (!desc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (desc.public) {
|
if (desc.public) {
|
||||||
this.setState({
|
this.setState({
|
||||||
sidePanelTitle: desc.public.fn,
|
sidePanelTitle: desc.public.fn,
|
||||||
sidePanelAvatar: makeImageUrl(desc.public.photo)
|
sidePanelAvatar: makeImageUrl(desc.public.photo)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.trusted) {
|
if (desc.trusted) {
|
||||||
const badges = [];
|
const badges = [];
|
||||||
for (const [key, val] of Object.entries(desc.trusted)) {
|
for (const [key, val] of Object.entries(desc.trusted)) {
|
||||||
@@ -859,11 +865,15 @@ class TinodeWeb extends React.Component {
|
|||||||
myTrustedBadges: badges,
|
myTrustedBadges: badges,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.acs) {
|
if (desc.acs) {
|
||||||
this.setState({
|
this.setState({
|
||||||
incognitoMode: !desc.acs.isPresencer()
|
incognitoMode: !desc.acs.isPresencer()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TinodeWeb.checkIfPinsUpdated(desc, this.state.chatList)) {
|
||||||
|
this.resetContactList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -961,7 +971,8 @@ class TinodeWeb extends React.Component {
|
|||||||
newState.ready = true;
|
newState.ready = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tinode.getMeTopic().contacts(c => {
|
const me = this.tinode.getMeTopic();
|
||||||
|
me.contacts(c => {
|
||||||
if (!c.topic && !c.user) {
|
if (!c.topic && !c.user) {
|
||||||
// Contacts expect c.topic to be set.
|
// Contacts expect c.topic to be set.
|
||||||
c.topic = c.name;
|
c.topic = c.name;
|
||||||
@@ -976,7 +987,10 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
const past = new Date(0);
|
const past = new Date(0);
|
||||||
newState.chatList.sort((a, b) => {
|
newState.chatList.sort((a, b) => {
|
||||||
return (a.touched || past).getTime() - (b.touched || past).getTime();
|
// Pinned topics take precedence.
|
||||||
|
const pins = me.pinnedTopicRank(b.topic) - me.pinnedTopicRank(a.topic);
|
||||||
|
// Then sort by last touched time.
|
||||||
|
return pins != 0 ? pins : (b.touched || past).getTime() - (a.touched || past).getTime();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Merge search results and chat list.
|
// Merge search results and chat list.
|
||||||
@@ -984,6 +998,16 @@ class TinodeWeb extends React.Component {
|
|||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static checkIfPinsUpdated(me, chatList) {
|
||||||
|
for (let i = 0; i < chatList.length; i++) {
|
||||||
|
const c = chatList[i];
|
||||||
|
if (!!me.pinnedTopicRank(c.topic) != c.pinned) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fnd topic: find contacts by tokens */
|
/* Fnd topic: find contacts by tokens */
|
||||||
tnInitFind() {
|
tnInitFind() {
|
||||||
const fnd = this.tinode.getFndTopic();
|
const fnd = this.tinode.getFndTopic();
|
||||||
@@ -1223,6 +1247,11 @@ class TinodeWeb extends React.Component {
|
|||||||
document.documentElement.style.setProperty('--message-text-size', `${size}pt`);
|
document.documentElement.style.setProperty('--message-text-size', `${size}pt`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSendOnEnter(option) {
|
||||||
|
this.setState({sendOnEnter: option});
|
||||||
|
LocalStorageUtil.updateObject('settings', {sendOnEnter: option});
|
||||||
|
}
|
||||||
|
|
||||||
handleUpdateAccountTagsRequest(_, tags) {
|
handleUpdateAccountTagsRequest(_, tags) {
|
||||||
this.tinode.getMeTopic().setMeta({tags: tags})
|
this.tinode.getMeTopic().setMeta({tags: tags})
|
||||||
.catch(err => this.handleError(err.message, 'err'));
|
.catch(err => this.handleError(err.message, 'err'));
|
||||||
@@ -1335,7 +1364,7 @@ class TinodeWeb extends React.Component {
|
|||||||
handleSidepanelCancel() {
|
handleSidepanelCancel() {
|
||||||
const parsed = HashNavigation.parseUrlHash(window.location.hash);
|
const parsed = HashNavigation.parseUrlHash(window.location.hash);
|
||||||
let path = '';
|
let path = '';
|
||||||
if (['security','support','general','notif','appearance'].includes(parsed.path[0])) {
|
if (['security','support','general','notif','acc_general'].includes(parsed.path[0])) {
|
||||||
path = 'edit';
|
path = 'edit';
|
||||||
} else if ('crop' == parsed.path[0]) {
|
} else if ('crop' == parsed.path[0]) {
|
||||||
path = 'general';
|
path = 'general';
|
||||||
@@ -1636,11 +1665,13 @@ class TinodeWeb extends React.Component {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const me = this.tinode.getMeTopic();
|
||||||
let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false,
|
let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false,
|
||||||
archived = false, webrtc = false, writer = false, p2p = false, self = false;
|
archived = false, pinned = false, webrtc = false, writer = false, p2p = false, self = false;
|
||||||
if (topic) {
|
if (topic) {
|
||||||
subscribed = topic.isSubscribed();
|
subscribed = topic.isSubscribed();
|
||||||
archived = topic.isArchived();
|
archived = topic.isArchived();
|
||||||
|
pinned = me.pinnedTopicRank(topicName) > 0;
|
||||||
|
|
||||||
const acs = topic.getAccessMode();
|
const acs = topic.getAccessMode();
|
||||||
if (acs) {
|
if (acs) {
|
||||||
@@ -1673,6 +1704,7 @@ class TinodeWeb extends React.Component {
|
|||||||
subscribed && deleter && !self ? 'messages_clear_hard' : null,
|
subscribed && deleter && !self ? 'messages_clear_hard' : null,
|
||||||
self ? null : (muted ? (blocked ? null : 'topic_unmute') : 'topic_mute'),
|
self ? null : (muted ? (blocked ? null : 'topic_unmute') : 'topic_mute'),
|
||||||
self ? null : (self_blocked ? 'topic_unblock' : 'topic_block'),
|
self ? null : (self_blocked ? 'topic_unblock' : 'topic_block'),
|
||||||
|
pinned ? 'topic_unpin' : 'topic_pin',
|
||||||
archived ? 'topic_restore' : 'topic_archive',
|
archived ? 'topic_restore' : 'topic_archive',
|
||||||
'topic_delete'
|
'topic_delete'
|
||||||
];
|
];
|
||||||
@@ -2084,6 +2116,7 @@ class TinodeWeb extends React.Component {
|
|||||||
reqCredMethod={this.state.reqCredMethod}
|
reqCredMethod={this.state.reqCredMethod}
|
||||||
textSize={this.state.textSize}
|
textSize={this.state.textSize}
|
||||||
colorSchema={this.state.colorSchema}
|
colorSchema={this.state.colorSchema}
|
||||||
|
sendOnEnter={this.state.sendOnEnter}
|
||||||
|
|
||||||
onGlobalSettings={this.handleGlobalSettings}
|
onGlobalSettings={this.handleGlobalSettings}
|
||||||
onSignUp={this.handleNewAccount}
|
onSignUp={this.handleNewAccount}
|
||||||
@@ -2100,6 +2133,7 @@ class TinodeWeb extends React.Component {
|
|||||||
onToggleIncognitoMode={this.handleToggleIncognitoMode}
|
onToggleIncognitoMode={this.handleToggleIncognitoMode}
|
||||||
onChangeColorSchema={this.handleChangeColorSchema}
|
onChangeColorSchema={this.handleChangeColorSchema}
|
||||||
onTextSizeChanged={this.handleChangeTextSize}
|
onTextSizeChanged={this.handleChangeTextSize}
|
||||||
|
onSendOnEnterChanged={this.handleSendOnEnter}
|
||||||
onCredAdd={this.handleCredAdd}
|
onCredAdd={this.handleCredAdd}
|
||||||
onCredDelete={this.handleCredDelete}
|
onCredDelete={this.handleCredDelete}
|
||||||
onCredConfirm={this.handleCredConfirm}
|
onCredConfirm={this.handleCredConfirm}
|
||||||
@@ -2142,7 +2176,7 @@ class TinodeWeb extends React.Component {
|
|||||||
serverAddress={this.state.serverAddress}
|
serverAddress={this.state.serverAddress}
|
||||||
applicationVisible={this.state.applicationVisible}
|
applicationVisible={this.state.applicationVisible}
|
||||||
colorSchema={this.state.colorSchema}
|
colorSchema={this.state.colorSchema}
|
||||||
|
sendOnEnter={this.state.sendOnEnter}
|
||||||
forwardMessage={this.state.forwardMessage}
|
forwardMessage={this.state.forwardMessage}
|
||||||
onCancelForwardMessage={this.handleHideForwardDialog}
|
onCancelForwardMessage={this.handleHideForwardDialog}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { MAX_TITLE_LENGTH, MAX_PEER_TITLE_LENGTH } from '../config.js';
|
|||||||
import { CALL_STATE_INCOMING_RECEIVED } from '../constants.js';
|
import { CALL_STATE_INCOMING_RECEIVED } from '../constants.js';
|
||||||
|
|
||||||
import { makeImageUrl } from '../lib/blob-helpers.js';
|
import { makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
import { clipStr } from '../lib/utils.js'
|
import { truncateString } from '../lib/strformat.js';
|
||||||
|
|
||||||
const RING_SOUND = new Audio('audio/call-in.m4a');
|
const RING_SOUND = new Audio('audio/call-in.m4a');
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ export default class CallIncoming extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
fullName: clipStr(topic.public ? topic.public.fn : undefined, MAX_TITLE_LENGTH),
|
fullName: truncateString(topic.public ? topic.public.fn : undefined, MAX_TITLE_LENGTH),
|
||||||
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
avatar: makeImageUrl(topic.public ? topic.public.photo : null),
|
||||||
trustedBadges: badges,
|
trustedBadges: badges,
|
||||||
});
|
});
|
||||||
@@ -127,10 +127,10 @@ export default class CallIncoming extends React.Component {
|
|||||||
topic={this.props.topic}
|
topic={this.props.topic}
|
||||||
title={this.state.fullName} />
|
title={this.state.fullName} />
|
||||||
</div>
|
</div>
|
||||||
<div className="caller-name">{clipStr(this.state.fullName, MAX_PEER_TITLE_LENGTH)}
|
<div className="caller-name">{truncateString(this.state.fullName, MAX_PEER_TITLE_LENGTH)}
|
||||||
<BadgeList short={true} trustedBadges={this.state.trustedBadges} /></div>
|
<BadgeList short={true} trustedBadges={this.state.trustedBadges} /></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="controls">
|
<div id="controls">
|
||||||
{this.props.callState == CALL_STATE_INCOMING_RECEIVED ?
|
{this.props.callState == CALL_STATE_INCOMING_RECEIVED ?
|
||||||
<>
|
<>
|
||||||
<button className="danger" onClick={this.handleRejectCall}>
|
<button className="danger" onClick={this.handleRejectCall}>
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import LetterTile from './letter-tile.jsx';
|
|||||||
|
|
||||||
import { MAX_PEER_TITLE_LENGTH } from '../config.js';
|
import { MAX_PEER_TITLE_LENGTH } from '../config.js';
|
||||||
import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constants.js';
|
import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constants.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
import { clipStr } from '../lib/utils.js'
|
|
||||||
|
|
||||||
const RING_SOUND = new Audio('audio/call-out.m4a');
|
const RING_SOUND = new Audio('audio/call-out.m4a');
|
||||||
RING_SOUND.loop = true;
|
RING_SOUND.loop = true;
|
||||||
@@ -53,8 +52,10 @@ class CallPanel extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
this.isOutgoingCall = props.callState == CALL_STATE_OUTGOING_INITATED;
|
this.isOutgoingCall = props.callState == CALL_STATE_OUTGOING_INITATED;
|
||||||
|
|
||||||
|
this.containerRef = React.createRef();
|
||||||
this.localRef = React.createRef();
|
this.localRef = React.createRef();
|
||||||
this.remoteRef = React.createRef();
|
this.remoteRef = React.createRef();
|
||||||
|
|
||||||
// Cache for remote ice candidates until initial setup gets completed.
|
// Cache for remote ice candidates until initial setup gets completed.
|
||||||
this.remoteIceCandidatesCache = [];
|
this.remoteIceCandidatesCache = [];
|
||||||
|
|
||||||
@@ -101,6 +102,53 @@ class CallPanel extends React.PureComponent {
|
|||||||
this.handleDataChannelMessage = this.handleDataChannelMessage.bind(this);
|
this.handleDataChannelMessage = this.handleDataChannelMessage.bind(this);
|
||||||
this.handleDataChannelOpen = this.handleDataChannelOpen.bind(this);
|
this.handleDataChannelOpen = this.handleDataChannelOpen.bind(this);
|
||||||
this.handleDataChannelClose = this.handleDataChannelClose.bind(this);
|
this.handleDataChannelClose = this.handleDataChannelClose.bind(this);
|
||||||
|
|
||||||
|
this.bounds = {};
|
||||||
|
|
||||||
|
this.dragStart = (event) => {
|
||||||
|
if (!event.target.classList.contains('draggable')) {
|
||||||
|
// Prevent dragging when clicking on buttons or other non-draggable elements.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (window.matchMedia('(width<=640px)')) {
|
||||||
|
// No dragging on small screens.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.props.minimized && this.containerRef) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.containerRef.current.setPointerCapture(event.pointerId);
|
||||||
|
this.containerRef.current.style.cursor = 'grabbing';
|
||||||
|
this.bounds.left = this.containerRef.current.offsetLeft;
|
||||||
|
this.bounds.top = this.containerRef.current.offsetTop;
|
||||||
|
this.bounds.width = this.containerRef.current.offsetWidth;
|
||||||
|
this.bounds.height = this.containerRef.current.offsetHeight;
|
||||||
|
|
||||||
|
const parent = this.containerRef.current.offsetParent;
|
||||||
|
this.bounds.maxX = parent.offsetWidth;
|
||||||
|
this.bounds.maxY = parent.offsetHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.drag = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const el = this.containerRef.current;
|
||||||
|
if (this.props.minimized && this.containerRef && el.hasPointerCapture(event.pointerId)) {
|
||||||
|
// Calculate new position. Restrict to the visible area of the parent element.
|
||||||
|
this.bounds.left = Math.min(Math.max(this.bounds.left + event.movementX, 0),
|
||||||
|
this.bounds.maxX - this.bounds.width);
|
||||||
|
this.bounds.top = Math.min(Math.max(this.bounds.top + event.movementY, 0),
|
||||||
|
this.bounds.maxY - this.bounds.height);
|
||||||
|
|
||||||
|
el.style.left = `${this.bounds.left}px`;
|
||||||
|
el.style.top = `${this.bounds.top}px`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.dragEnd = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.props.minimized && this.containerRef) {
|
||||||
|
this.containerRef.current.releasePointerCapture(event.pointerId);
|
||||||
|
this.containerRef.current.style.cursor = 'grab';
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -111,12 +159,24 @@ class CallPanel extends React.PureComponent {
|
|||||||
this.props.callState == CALL_STATE_IN_PROGRESS) && this.localRef.current) {
|
this.props.callState == CALL_STATE_IN_PROGRESS) && this.localRef.current) {
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.containerRef) {
|
||||||
|
this.containerRef.current.addEventListener('pointerdown', this.dragStart);
|
||||||
|
this.containerRef.current.addEventListener('pointermove', this.drag);
|
||||||
|
this.containerRef.current.addEventListener('pointerup', this.dragEnd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const topic = this.props.tinode.getTopic(this.props.topic);
|
const topic = this.props.tinode.getTopic(this.props.topic);
|
||||||
topic.onInfo = this.previousOnInfo;
|
topic.onInfo = this.previousOnInfo;
|
||||||
this.stop();
|
this.stop();
|
||||||
|
|
||||||
|
if (this.containerRef) {
|
||||||
|
this.containerRef.current.removeEventListener('pointerdown', this.dragStart);
|
||||||
|
this.containerRef.current.removeEventListener('pointermove', this.drag);
|
||||||
|
this.containerRef.current.removeEventListener('pointerup', this.dragEnd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVideoCallAccepted(info) {
|
handleVideoCallAccepted(info) {
|
||||||
@@ -584,7 +644,7 @@ class CallPanel extends React.PureComponent {
|
|||||||
const disabled = !this.state.pc || !this.state.dataChannel || !(audioTracks && audioTracks[0]);
|
const disabled = !this.state.pc || !this.state.dataChannel || !(audioTracks && audioTracks[0]);
|
||||||
const audioIcon = audioTracks && audioTracks[0] && audioTracks[0].enabled ? 'mic' : 'mic_off';
|
const audioIcon = audioTracks && audioTracks[0] && audioTracks[0].enabled ? 'mic' : 'mic_off';
|
||||||
const videoIcon = videoTracks && videoTracks[0] && videoTracks[0].enabled && videoTracks[0].readyState == 'live' ? 'videocam' : 'videocam_off';
|
const videoIcon = videoTracks && videoTracks[0] && videoTracks[0].enabled && videoTracks[0].readyState == 'live' ? 'videocam' : 'videocam_off';
|
||||||
const peerTitle = clipStr(this.props.title, MAX_PEER_TITLE_LENGTH);
|
const peerTitle = truncateString(this.props.title, MAX_PEER_TITLE_LENGTH);
|
||||||
const pulseAnimation = this.state.waitingForPeer ? ' pulse' : '';
|
const pulseAnimation = this.state.waitingForPeer ? ' pulse' : '';
|
||||||
|
|
||||||
let remoteActive = false;
|
let remoteActive = false;
|
||||||
@@ -596,11 +656,13 @@ class CallPanel extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const minimizedClass = this.props.minimized ? 'minimized' : null;
|
||||||
|
const fullScreen = this.props.minimized ? 'fullscreen' : 'fullscreen_exit';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div id="video-container" className={minimizedClass} ref={this.containerRef}>
|
||||||
<div id="video-container">
|
|
||||||
<div id="video-container-panel">
|
<div id="video-container-panel">
|
||||||
<div className="call-party self" disabled={this.state.audioOnly}>
|
<div className="call-party self" disabled={this.state.audioOnly || this.props.minimized}>
|
||||||
<video ref={this.localRef} autoPlay muted playsInline />
|
<video ref={this.localRef} autoPlay muted playsInline />
|
||||||
<div className="caller-name inactive">
|
<div className="caller-name inactive">
|
||||||
<FormattedMessage id="calls_you_label"
|
<FormattedMessage id="calls_you_label"
|
||||||
@@ -615,7 +677,7 @@ class CallPanel extends React.PureComponent {
|
|||||||
</> :
|
</> :
|
||||||
<>
|
<>
|
||||||
<audio ref={this.remoteRef} autoPlay />
|
<audio ref={this.remoteRef} autoPlay />
|
||||||
<div className={`caller-card${pulseAnimation}`}>
|
<div className={`caller-card${pulseAnimation} draggable`}>
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
<LetterTile
|
<LetterTile
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
@@ -629,7 +691,10 @@ class CallPanel extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="controls">
|
<button className="full-screen" onClick={this.props.onToggleMinimize}>
|
||||||
|
<i className="material-icons">{fullScreen}</i>
|
||||||
|
</button>
|
||||||
|
<div id="controls" className={minimizedClass}>
|
||||||
<button className="danger" onClick={this.handleCloseClick}>
|
<button className="danger" onClick={this.handleCloseClick}>
|
||||||
<i className="material-icons">call_end</i>
|
<i className="material-icons">call_end</i>
|
||||||
</button>
|
</button>
|
||||||
@@ -641,7 +706,6 @@ class CallPanel extends React.PureComponent {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ class ContactList extends React.Component {
|
|||||||
badges={badges}
|
badges={badges}
|
||||||
showCheckmark={showCheckmark}
|
showCheckmark={showCheckmark}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
pinned={c.pinned}
|
||||||
showOnline={this.props.showOnline && !isChannel}
|
showOnline={this.props.showOnline && !isChannel}
|
||||||
isChannel={isChannel}
|
isChannel={isChannel}
|
||||||
isGroup={isGroup}
|
isGroup={isGroup}
|
||||||
|
|||||||
+12
-4
@@ -12,6 +12,7 @@ import { Drafty } from 'tinode-sdk';
|
|||||||
|
|
||||||
import { previewFormatter } from '../lib/formatters.js';
|
import { previewFormatter } from '../lib/formatters.js';
|
||||||
import { deliveryMarker } from '../lib/utils.js';
|
import { deliveryMarker } from '../lib/utils.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
self_topic_name: {
|
self_topic_name: {
|
||||||
@@ -46,7 +47,12 @@ class Contact extends React.Component {
|
|||||||
handleContextClick(e) {
|
handleContextClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.showContextMenu({ topicName: this.props.item, y: e.pageY, x: e.pageX });
|
this.props.showContextMenu({
|
||||||
|
topicName: this.props.item,
|
||||||
|
topicTitle: this.props.title,
|
||||||
|
y: e.pageY,
|
||||||
|
x: e.pageX
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -60,8 +66,8 @@ class Contact extends React.Component {
|
|||||||
description="Title shown when the topic has no name" /></i>;
|
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.
|
// Have to support RTL languages too.
|
||||||
title = title.substring(0, 28) + '…';
|
title = truncateString(title, 30);
|
||||||
}
|
}
|
||||||
const online = this.props.now ? 'online' : 'offline';
|
const online = this.props.now ? 'online' : 'offline';
|
||||||
const avatar = this.props.avatar ? this.props.avatar : true;
|
const avatar = this.props.avatar ? this.props.avatar : true;
|
||||||
@@ -110,8 +116,10 @@ class Contact extends React.Component {
|
|||||||
|
|
||||||
const comment = preview || this.props.comment ||
|
const comment = preview || this.props.comment ||
|
||||||
(self ? this.props.intl.formatMessage(messages.self_topic_comment) : '\u00A0');
|
(self ? this.props.intl.formatMessage(messages.self_topic_comment) : '\u00A0');
|
||||||
|
const bgColor = this.props.showCheckmark ? null :
|
||||||
|
(this.props.selected ? 'selected' : (this.props.pinned ? 'tpinned' : null));
|
||||||
return (
|
return (
|
||||||
<li className={!this.props.showCheckmark && this.props.selected ? 'selected' : null} onClick={this.handleClick}>
|
<li className={bgColor} onClick={this.handleClick}>
|
||||||
<div className="avatar-box">
|
<div className="avatar-box">
|
||||||
<LetterTile
|
<LetterTile
|
||||||
tinode={this.props.tinode}
|
tinode={this.props.tinode}
|
||||||
|
|||||||
@@ -126,6 +126,16 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Unpin',
|
defaultMessage: 'Unpin',
|
||||||
description: 'Menu item [Unpin] for un-pinning the message.'
|
description: 'Menu item [Unpin] for un-pinning the message.'
|
||||||
},
|
},
|
||||||
|
pin_chat: {
|
||||||
|
id: 'pin_chat',
|
||||||
|
defaultMessage: 'Pin',
|
||||||
|
description: 'Menu item [Pin] for pinning chat to the top.'
|
||||||
|
},
|
||||||
|
unpin_chat: {
|
||||||
|
id: 'unpin_chat',
|
||||||
|
defaultMessage: 'Unpin',
|
||||||
|
description: 'Menu item [Unpin] for un-pinning the chat.'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class ContextMenu extends React.Component {
|
class ContextMenu extends React.Component {
|
||||||
@@ -152,7 +162,7 @@ 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.clear_messages), // title
|
params.topicTitle || formatMessage(messages.clear_messages), // title
|
||||||
formatMessage(messages.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"
|
||||||
@@ -166,7 +176,7 @@ 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.clear_for_all), // title
|
params.topicTitle || formatMessage(messages.clear_for_all), // title
|
||||||
formatMessage(messages.delete_messages_warning), // content
|
formatMessage(messages.delete_messages_warning), // content
|
||||||
(_ => { return this.deleteMessages(true, true, params, errorHandler); }),
|
(_ => { return this.deleteMessages(true, true, params, errorHandler); }),
|
||||||
null, // "OK"
|
null, // "OK"
|
||||||
@@ -257,7 +267,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.block), // title
|
params.topicTitle || formatMessage(messages.block), // title
|
||||||
formatMessage(messages.topic_block_warning), // content
|
formatMessage(messages.topic_block_warning), // content
|
||||||
(_ => this.topicPermissionSetter('-JP', params, errorHandler).then(ctrl => {
|
(_ => this.topicPermissionSetter('-JP', params, errorHandler).then(ctrl => {
|
||||||
this.props.onTopicRemoved(params.topicName);
|
this.props.onTopicRemoved(params.topicName);
|
||||||
@@ -274,7 +284,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.topic_delete), // title
|
params.topicTitle || 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);
|
||||||
@@ -326,6 +336,38 @@ class ContextMenu extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'topic_pin': {
|
||||||
|
id: 'topic_pin',
|
||||||
|
title: formatMessage(messages.pin_chat),
|
||||||
|
handler: (params, errorHandler) => {
|
||||||
|
const me = this.props.tinode.getMeTopic();
|
||||||
|
if (!me) {
|
||||||
|
console.warn("'me' topic not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return me.pinTopic(params.topicName, true).catch(err => {
|
||||||
|
if (errorHandler) {
|
||||||
|
errorHandler(err.message, 'err');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'topic_unpin': {
|
||||||
|
id: 'topic_unpin',
|
||||||
|
title: formatMessage(messages.unpin_chat),
|
||||||
|
handler: (params, errorHandler) => {
|
||||||
|
const me = this.props.tinode.getMeTopic();
|
||||||
|
if (!me) {
|
||||||
|
console.warn("'me' topic not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return me.pinTopic(params.topicName, false).catch(err => {
|
||||||
|
if (errorHandler) {
|
||||||
|
errorHandler(err.message, 'err');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
// menu_item_edit_permissions is defined elsewhere.
|
// menu_item_edit_permissions is defined elsewhere.
|
||||||
'permissions': {
|
'permissions': {
|
||||||
id: 'permissions',
|
id: 'permissions',
|
||||||
|
|||||||
+25
-21
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const DEFAULT_MAX_ZOOM = 2.5;
|
const DEFAULT_MAX_ZOOM = 8;
|
||||||
|
|
||||||
export default class Cropper extends React.Component {
|
export default class Cropper extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// Coordinates of the top left corner of the image in container coordinates.
|
// Coordinates of the top left corner of the image in bounding box (container) coordinates.
|
||||||
panX: 0,
|
panX: 0,
|
||||||
panY: 0,
|
panY: 0,
|
||||||
// Origin of the zoom in image coordinates.
|
// Origin of the zoom in image coordinates.
|
||||||
@@ -18,7 +18,7 @@ export default class Cropper extends React.Component {
|
|||||||
// Zoom scale.
|
// Zoom scale.
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
// Zoom min and max.
|
// Zoom min and max.
|
||||||
minZoom: 0,
|
minZoom: 0.001,
|
||||||
maxZoom: DEFAULT_MAX_ZOOM,
|
maxZoom: DEFAULT_MAX_ZOOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export default class Cropper extends React.Component {
|
|||||||
// The main container (static).
|
// The main container (static).
|
||||||
this.boundingBox = React.createRef();
|
this.boundingBox = React.createRef();
|
||||||
|
|
||||||
// The original image width and height before any scaling.
|
// The original image width and height before any scaling (does not change).
|
||||||
this.imageWidth = 0;
|
this.imageWidth = 0;
|
||||||
this.imageHeight = 0;
|
this.imageHeight = 0;
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ export default class Cropper extends React.Component {
|
|||||||
// Length of the previous mouse move when dragging.
|
// Length of the previous mouse move when dragging.
|
||||||
this.prevDistance = 0;
|
this.prevDistance = 0;
|
||||||
|
|
||||||
// Bounding rectangles of static elements.
|
// Bounding rectangles of static elements (does not change).
|
||||||
this.cutoutRect = {};
|
this.cutoutRect = {};
|
||||||
this.bBoxRect = {};
|
this.bBoxRect = {};
|
||||||
// Center of the bounding box.
|
// Center of the bounding box (does not change).
|
||||||
this.originX = 0;
|
this.originX = 0;
|
||||||
this.originY = 0;
|
this.originY = 0;
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ export default class Cropper extends React.Component {
|
|||||||
this.originX = this.bBoxRect.width / 2;
|
this.originX = this.bBoxRect.width / 2;
|
||||||
this.originY = this.bBoxRect.height / 2;
|
this.originY = this.bBoxRect.height / 2;
|
||||||
|
|
||||||
// The rectangle position is in viewport coordinates.
|
// The cutout circle bounds in viewport coordinates.
|
||||||
this.cutoutRect = this.cutout.current.getBoundingClientRect();
|
this.cutoutRect = this.cutout.current.getBoundingClientRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ export default class Cropper extends React.Component {
|
|||||||
const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);
|
const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);
|
||||||
this.setState({
|
this.setState({
|
||||||
minZoom: minZoom,
|
minZoom: minZoom,
|
||||||
maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)
|
maxZoom: minZoom * DEFAULT_MAX_ZOOM
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. "fill" not "fit".
|
// Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. "fill" not "fit".
|
||||||
@@ -150,25 +150,29 @@ export default class Cropper extends React.Component {
|
|||||||
let panY = this.state.panY;
|
let panY = this.state.panY;
|
||||||
|
|
||||||
// Ensure that the image at the new zoom is not outside of the cutout boundaries.
|
// Ensure that the image at the new zoom is not outside of the cutout boundaries.
|
||||||
|
|
||||||
// Calculate image coordinates at the new zoom.
|
// Calculate image coordinates at the new zoom.
|
||||||
const imgLeft = this.originX - (this.originX - panX) * zoom;
|
const imgLeft = this.originX - (this.originX - panX) * zoom;
|
||||||
const imgRight = imgLeft + this.imageWidth * zoom;
|
const imgRight = imgLeft + this.imageWidth * zoom;
|
||||||
|
const imgTop = this.originY - (this.originY - panY) * zoom;
|
||||||
|
const imgBottom = imgTop + this.imageHeight * zoom;
|
||||||
|
|
||||||
// Cutout in the same coordinates as the image.
|
// Cutout in the same coordinates as the image.
|
||||||
const coLeft = this.cutoutRect.left - this.bBoxRect.left;
|
const coLeft = this.cutoutRect.left - this.bBoxRect.left;
|
||||||
const coRight = coLeft + this.cutoutRect.width;
|
const coRight = coLeft + this.cutoutRect.width;
|
||||||
if (coLeft < imgLeft) {
|
|
||||||
panX -= imgLeft - coLeft;
|
|
||||||
} else if (coRight > imgRight) {
|
|
||||||
panX += coRight - imgRight;
|
|
||||||
}
|
|
||||||
const imgTop = this.originY - (this.originY - panY) * zoom;
|
|
||||||
const imgBottom = imgTop + this.imageHeight * zoom;
|
|
||||||
const coTop = this.cutoutRect.top - this.bBoxRect.top;
|
const coTop = this.cutoutRect.top - this.bBoxRect.top;
|
||||||
const coBottom = coTop + this.cutoutRect.height;
|
const coBottom = coTop + this.cutoutRect.height;
|
||||||
|
|
||||||
|
if (coLeft < imgLeft) {
|
||||||
|
panX -= (imgLeft - coLeft) / zoom;
|
||||||
|
} else if (coRight > imgRight) {
|
||||||
|
panX += (coRight - imgRight) / zoom;
|
||||||
|
}
|
||||||
|
|
||||||
if (coTop < imgTop) {
|
if (coTop < imgTop) {
|
||||||
panY -= imgTop - coTop;
|
panY -= (imgTop - coTop) / zoom;
|
||||||
} else if (coBottom > imgBottom) {
|
} else if (coBottom > imgBottom) {
|
||||||
panY += coBottom - imgBottom;
|
panY += (coBottom - imgBottom) /zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.positionAll(panX, panY, zoom);
|
this.positionAll(panX, panY, zoom);
|
||||||
@@ -193,8 +197,8 @@ export default class Cropper extends React.Component {
|
|||||||
|
|
||||||
// Perform image panning.
|
// Perform image panning.
|
||||||
translate(pageX, pageY) {
|
translate(pageX, pageY) {
|
||||||
const dX = pageX - this.mouseX;
|
const dX = (pageX - this.mouseX) / this.state.zoom;
|
||||||
const dY = pageY - this.mouseY;
|
const dY = (pageY - this.mouseY) / this.state.zoom;
|
||||||
|
|
||||||
this.mouseX = pageX;
|
this.mouseX = pageX;
|
||||||
this.mouseY = pageY;
|
this.mouseY = pageY;
|
||||||
@@ -203,9 +207,9 @@ export default class Cropper extends React.Component {
|
|||||||
const imgRect = this.preview.current.getBoundingClientRect();
|
const imgRect = this.preview.current.getBoundingClientRect();
|
||||||
|
|
||||||
// Check if the new position is within the boundaries, and if not if it's closer to them.
|
// Check if the new position is within the boundaries, and if not if it's closer to them.
|
||||||
let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],
|
const panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],
|
||||||
[this.cutoutRect.left, this.cutoutRect.right], dX);
|
[this.cutoutRect.left, this.cutoutRect.right], dX);
|
||||||
let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],
|
const panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],
|
||||||
[this.cutoutRect.top, this.cutoutRect.bottom], dY);
|
[this.cutoutRect.top, this.cutoutRect.bottom], dY);
|
||||||
|
|
||||||
this.positionAll(panX, panY, this.state.zoom);
|
this.positionAll(panX, panY, this.state.zoom);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import TagManager from './tag-manager.jsx';
|
|||||||
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LENGTH,
|
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LENGTH,
|
||||||
MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
|
MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
|
||||||
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
|
|
||||||
export default class NewTopicGroup extends React.PureComponent {
|
export default class NewTopicGroup extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -100,9 +101,9 @@ export default class NewTopicGroup extends React.PureComponent {
|
|||||||
handleSubmit(e) {
|
handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const fn = this.state.fullName.trim().substring(0, MAX_TITLE_LENGTH);
|
const fn = truncateString(this.state.fullName.trim(), MAX_TITLE_LENGTH);
|
||||||
const comment = this.state.private.trim().substring(0, MAX_TITLE_LENGTH);
|
const comment = truncateString(this.state.private.trim(), MAX_TITLE_LENGTH);
|
||||||
const description = this.state.description.trim().substring(0, MAX_TOPIC_DESCRIPTION_LENGTH);
|
const description = truncateString(this.state.description.trim(), MAX_TOPIC_DESCRIPTION_LENGTH);
|
||||||
if (fn) {
|
if (fn) {
|
||||||
this.props.onSubmit(fn, description, this.state.imageUrl, comment, this.state.tags, this.state.isChannel);
|
this.props.onSubmit(fn, description, this.state.imageUrl, comment, this.state.tags, this.state.isChannel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,8 +200,9 @@ class SendMessage extends React.PureComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove this if you don't want Enter to trigger send
|
// Enter pressed: either send, or enter a new line.
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
|
if (this.props.sendOnEnter == 'plain') {
|
||||||
// Have Shift-Enter insert a line break instead
|
// Have Shift-Enter insert a line break instead
|
||||||
if (!e.shiftKey) {
|
if (!e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -209,6 +210,14 @@ class SendMessage extends React.PureComponent {
|
|||||||
|
|
||||||
this.handleSend(e);
|
this.handleSend(e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (e.ctrlKey || e.metaKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.handleSend(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import TagManager from './tag-manager.jsx';
|
|||||||
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LENGTH,
|
import { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LENGTH,
|
||||||
MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
|
MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';
|
||||||
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
import { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';
|
||||||
|
import { truncateString } from '../lib/strformat.js';
|
||||||
import { arrayEqual, theCard } from '../lib/utils.js';
|
import { arrayEqual, theCard } from '../lib/utils.js';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
@@ -98,7 +99,7 @@ class TopicDescEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleFullNameUpdate(fn) {
|
handleFullNameUpdate(fn) {
|
||||||
fn = fn.trim().substring(0, MAX_TITLE_LENGTH);
|
fn = truncateString(fn.trim(), MAX_TITLE_LENGTH);
|
||||||
if (fn && this.state.fullName !== fn) {
|
if (fn && this.state.fullName !== fn) {
|
||||||
this.setState({fullName: fn});
|
this.setState({fullName: fn});
|
||||||
this.props.onUpdateTopicDesc(this.props.topic, theCard(fn, null));
|
this.props.onUpdateTopicDesc(this.props.topic, theCard(fn, null));
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+513
-98
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -145,7 +145,7 @@ class AccSecurityView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Comp
|
|||||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||||
className: "panel-form-row"
|
className: "panel-form-row"
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", {
|
||||||
className: "small"
|
className: "small gray"
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||||
id: "incognito_mode_description",
|
id: "incognito_mode_description",
|
||||||
defaultMessage: [{
|
defaultMessage: [{
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -28,9 +28,11 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony import */ var libphonenumber_js_mobile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! libphonenumber-js/mobile */ "./node_modules/libphonenumber-js/mobile/exports/parsePhoneNumberWithError.js");
|
/* harmony import */ var libphonenumber_js_mobile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! libphonenumber-js/mobile */ "./node_modules/libphonenumber-js/mobile/exports/parsePhoneNumberWithError.js");
|
||||||
/* harmony import */ var _widgets_avatar_upload_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../widgets/avatar-upload.jsx */ "./src/widgets/avatar-upload.jsx");
|
/* harmony import */ var _widgets_avatar_upload_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../widgets/avatar-upload.jsx */ "./src/widgets/avatar-upload.jsx");
|
||||||
/* harmony import */ var _widgets_badge_list_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../widgets/badge-list.jsx */ "./src/widgets/badge-list.jsx");
|
/* harmony import */ var _widgets_badge_list_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../widgets/badge-list.jsx */ "./src/widgets/badge-list.jsx");
|
||||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
/* harmony import */ var _widgets_credential_edit_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../widgets/credential-edit.jsx */ "./src/widgets/credential-edit.jsx");
|
||||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||||||
/* harmony import */ var _widgets_credential_edit_jsx__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../widgets/credential-edit.jsx */ "./src/widgets/credential-edit.jsx");
|
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||||
|
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -40,16 +42,16 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
|
|
||||||
|
|
||||||
function _clip(str, length) {
|
function _clip(str, length) {
|
||||||
return str && str.substring(0, length);
|
return (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_7__.truncateString)(str, length);
|
||||||
}
|
}
|
||||||
class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const me = this.props.tinode.getMeTopic();
|
const me = this.props.tinode.getMeTopic();
|
||||||
this.state = {
|
this.state = {
|
||||||
fullName: _clip(me.public ? me.public.fn : undefined, _config_js__WEBPACK_IMPORTED_MODULE_6__.MAX_TITLE_LENGTH),
|
fullName: _clip(me.public ? me.public.fn : undefined, _config_js__WEBPACK_IMPORTED_MODULE_8__.MAX_TITLE_LENGTH),
|
||||||
description: _clip(me.public ? me.public.note : undefined, _config_js__WEBPACK_IMPORTED_MODULE_6__.MAX_TOPIC_DESCRIPTION_LENGTH),
|
description: _clip(me.public ? me.public.note : undefined, _config_js__WEBPACK_IMPORTED_MODULE_8__.MAX_TOPIC_DESCRIPTION_LENGTH),
|
||||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_5__.makeImageUrl)(me.public ? me.public.photo : null),
|
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_6__.makeImageUrl)(me.public ? me.public.photo : null),
|
||||||
credentials: me.getCredentials() || [],
|
credentials: me.getCredentials() || [],
|
||||||
credEdit: undefined,
|
credEdit: undefined,
|
||||||
alias: ((me.tags() || []).find(tag => tag.startsWith('alias:')) || '').substring(6)
|
alias: ((me.tags() || []).find(tag => tag.startsWith('alias:')) || '').substring(6)
|
||||||
@@ -67,7 +69,7 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
|
|||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
if (this.state.credEdit) {
|
if (this.state.credEdit) {
|
||||||
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_credential_edit_jsx__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_credential_edit_jsx__WEBPACK_IMPORTED_MODULE_5__["default"], {
|
||||||
method: this.state.credEdit.meth,
|
method: this.state.credEdit.meth,
|
||||||
val: this.state.credEdit.val,
|
val: this.state.credEdit.val,
|
||||||
done: this.state.credEdit.done,
|
done: this.state.credEdit.done,
|
||||||
@@ -219,15 +221,15 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
|
|||||||
className: "flat-button",
|
className: "flat-button",
|
||||||
onClick: e => {
|
onClick: e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onNavigate('appearance');
|
this.props.onNavigate('acc_general');
|
||||||
}
|
}
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||||
className: "material-icons"
|
className: "material-icons"
|
||||||
}, "tune"), "\xA0", react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
}, "tune"), "\xA0", react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||||
id: "sidepanel_title_appearance",
|
id: "sidepanel_title_acc_general",
|
||||||
defaultMessage: [{
|
defaultMessage: [{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Appearance"
|
"value": "General"
|
||||||
}]
|
}]
|
||||||
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||||
href: "#",
|
href: "#",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -33,7 +33,9 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony import */ var _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../lib/local-storage.js */ "./src/lib/local-storage.js");
|
/* harmony import */ var _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../lib/local-storage.js */ "./src/lib/local-storage.js");
|
||||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||||||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||||
|
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -109,6 +111,12 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
newAvatar: img,
|
newAvatar: img,
|
||||||
newAvatarMime: mime
|
newAvatarMime: mime
|
||||||
});
|
});
|
||||||
|
if (!img) {
|
||||||
|
this.setState({
|
||||||
|
imageUrl: null,
|
||||||
|
uploadUrl: null
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleToggleSaveToken() {
|
handleToggleSaveToken() {
|
||||||
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_7__["default"].setObject('keep-logged-in', !this.state.saveToken);
|
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_7__["default"].setObject('keep-logged-in', !this.state.saveToken);
|
||||||
@@ -118,7 +126,7 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
}
|
}
|
||||||
handleSubmit(e) {
|
handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onCreateAccount(this.state.login.trim(), this.state.password.trim(), (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_9__.theCard)(this.state.fn.trim().substring(0, _config_js__WEBPACK_IMPORTED_MODULE_10__.MAX_TITLE_LENGTH), this.state.uploadUrl), {
|
this.props.onCreateAccount(this.state.login.trim(), this.state.password.trim(), (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_9__.theCard)((0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_10__.truncateString)(this.state.fn.trim(), _config_js__WEBPACK_IMPORTED_MODULE_11__.MAX_TITLE_LENGTH), this.state.uploadUrl), {
|
||||||
'meth': this.state.meth,
|
'meth': this.state.meth,
|
||||||
'val': this.state.meth == 'email' ? this.state.email : this.state.meth == 'tel' ? this.state.tel : null
|
'val': this.state.meth == 'email' ? this.state.email : this.state.meth == 'tel' ? this.state.tel : null
|
||||||
});
|
});
|
||||||
@@ -126,7 +134,6 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
handleAvatarCropped(mime, blob, width, height) {
|
handleAvatarCropped(mime, blob, width, height) {
|
||||||
const url = blob ? URL.createObjectURL(blob) : null;
|
const url = blob ? URL.createObjectURL(blob) : null;
|
||||||
this.setState({
|
this.setState({
|
||||||
avatar: url,
|
|
||||||
newAvatar: null,
|
newAvatar: null,
|
||||||
newAvatarMime: null
|
newAvatarMime: null
|
||||||
});
|
});
|
||||||
@@ -150,7 +157,7 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
imageUrl: URL.createObjectURL(blob),
|
imageUrl: URL.createObjectURL(blob),
|
||||||
buttonDisabled: true
|
buttonDisabled: true
|
||||||
});
|
});
|
||||||
if (blob.size > _config_js__WEBPACK_IMPORTED_MODULE_10__.MAX_AVATAR_BYTES) {
|
if (blob.size > _config_js__WEBPACK_IMPORTED_MODULE_11__.MAX_AVATAR_BYTES) {
|
||||||
const uploader = this.props.tinode.getLargeFileHelper();
|
const uploader = this.props.tinode.getLargeFileHelper();
|
||||||
uploader.upload(blob, 'newacc').then(url => this.setState({
|
uploader.upload(blob, 'newacc').then(url => this.setState({
|
||||||
uploadUrl: url
|
uploadUrl: url
|
||||||
@@ -168,8 +175,8 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (width > _config_js__WEBPACK_IMPORTED_MODULE_10__.AVATAR_SIZE || height > _config_js__WEBPACK_IMPORTED_MODULE_10__.AVATAR_SIZE || width != height) {
|
if (width > _config_js__WEBPACK_IMPORTED_MODULE_11__.AVATAR_SIZE || height > _config_js__WEBPACK_IMPORTED_MODULE_11__.AVATAR_SIZE || width != height) {
|
||||||
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.imageScaled)(blob, _config_js__WEBPACK_IMPORTED_MODULE_10__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_10__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_10__.MAX_EXTERN_ATTACHMENT_SIZE, true).then(scaled => readyToUpload(scaled)).catch(err => this.props.onError(err.message, 'err'));
|
(0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_8__.imageScaled)(blob, _config_js__WEBPACK_IMPORTED_MODULE_11__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_11__.AVATAR_SIZE, _config_js__WEBPACK_IMPORTED_MODULE_11__.MAX_EXTERN_ATTACHMENT_SIZE, true).then(scaled => readyToUpload(scaled)).catch(err => this.props.onError(err.message, 'err'));
|
||||||
} else {
|
} else {
|
||||||
readyToUpload({
|
readyToUpload({
|
||||||
mime: mime,
|
mime: mime,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -28,6 +28,8 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
|
||||||
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
|
||||||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||||||
|
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -183,6 +185,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
previousMetaDesc: undefined,
|
previousMetaDesc: undefined,
|
||||||
previousSubsUpdated: undefined
|
previousSubsUpdated: undefined
|
||||||
};
|
};
|
||||||
|
this.propsUpdated = this.propsUpdated.bind(this);
|
||||||
this.resetSubs = this.resetSubs.bind(this);
|
this.resetSubs = this.resetSubs.bind(this);
|
||||||
this.resetDesc = this.resetDesc.bind(this);
|
this.resetDesc = this.resetDesc.bind(this);
|
||||||
this.resetTags = this.resetTags.bind(this);
|
this.resetTags = this.resetTags.bind(this);
|
||||||
@@ -201,7 +204,7 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
this.handleContextMenu = this.handleContextMenu.bind(this);
|
this.handleContextMenu = this.handleContextMenu.bind(this);
|
||||||
this.handleBackNavigate = this.handleBackNavigate.bind(this);
|
this.handleBackNavigate = this.handleBackNavigate.bind(this);
|
||||||
}
|
}
|
||||||
componentDidUpdate(props) {
|
propsUpdated(props) {
|
||||||
const topic = this.props.tinode.getTopic(props.topic);
|
const topic = this.props.tinode.getTopic(props.topic);
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
return;
|
return;
|
||||||
@@ -221,6 +224,12 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
this.resetTags(topic);
|
this.resetTags(topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
this.propsUpdated(this.props);
|
||||||
|
}
|
||||||
|
componentDidUpdate(props) {
|
||||||
|
this.propsUpdated(props);
|
||||||
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const topic = this.props.tinode.getTopic(this.props.topic);
|
const topic = this.props.tinode.getTopic(this.props.topic);
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
@@ -270,11 +279,11 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
sharer: acs && acs.isSharer(),
|
sharer: acs && acs.isSharer(),
|
||||||
deleter: acs && acs.isDeleter(),
|
deleter: acs && acs.isDeleter(),
|
||||||
muted: acs && acs.isMuted(),
|
muted: acs && acs.isMuted(),
|
||||||
fullName: isSelf ? props.intl.formatMessage(messages.self_topic_name) : (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.public && topic.public.fn, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
fullName: isSelf ? props.intl.formatMessage(messages.self_topic_name) : (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.truncateString)(topic.public && topic.public.fn, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
||||||
description: isSelf ? props.intl.formatMessage(messages.self_topic_comment) : (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.public && topic.public.note, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TOPIC_DESCRIPTION_LENGTH),
|
description: isSelf ? props.intl.formatMessage(messages.self_topic_comment) : (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.truncateString)(topic.public && topic.public.note, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TOPIC_DESCRIPTION_LENGTH),
|
||||||
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public ? topic.public.photo : null),
|
avatar: (0,_lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_15__.makeImageUrl)(topic.public ? topic.public.photo : null),
|
||||||
trustedBadges: badges,
|
trustedBadges: badges,
|
||||||
private: (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_16__.clipStr)(topic.private && topic.private.comment, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
private: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_17__.truncateString)(topic.private && topic.private.comment, _config_js__WEBPACK_IMPORTED_MODULE_14__.MAX_TITLE_LENGTH),
|
||||||
archived: topic.isArchived(),
|
archived: topic.isArchived(),
|
||||||
address: topic.name,
|
address: topic.name,
|
||||||
groupTopic: topic.isGroupType(),
|
groupTopic: topic.isGroupType(),
|
||||||
@@ -700,6 +709,24 @@ class InfoView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
className: "group"
|
className: "group"
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_badge_list_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_badge_list_jsx__WEBPACK_IMPORTED_MODULE_4__["default"], {
|
||||||
trustedBadges: this.state.trustedBadges
|
trustedBadges: this.state.trustedBadges
|
||||||
|
})) : null, this.state.groupTopic && topic.subcnt > 0 ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||||
|
className: "group"
|
||||||
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||||
|
className: "small"
|
||||||
|
}, this.state.channel ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||||
|
id: "label_subscriber_count",
|
||||||
|
defaultMessage: [{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Subscribers:"
|
||||||
|
}]
|
||||||
|
}) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
|
||||||
|
id: "label_member_count",
|
||||||
|
defaultMessage: [{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Members:"
|
||||||
|
}]
|
||||||
|
})), "\xA0", react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedNumber, {
|
||||||
|
value: topic.subcnt
|
||||||
})) : null, this.state.description ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
})) : null, this.state.description ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||||
className: "group"
|
className: "group"
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+52
-8
@@ -3753,6 +3753,38 @@ class TopicMe extends _topic_js__WEBPACK_IMPORTED_MODULE_2__["default"] {
|
|||||||
getCredentials() {
|
getCredentials() {
|
||||||
return this._credentials;
|
return this._credentials;
|
||||||
}
|
}
|
||||||
|
pinTopic(topic, pin) {
|
||||||
|
if (!this._attached) {
|
||||||
|
return Promise.reject(new Error("Cannot pin topic in inactive 'me' topic"));
|
||||||
|
}
|
||||||
|
if (!_topic_js__WEBPACK_IMPORTED_MODULE_2__["default"].isCommTopicName(topic)) {
|
||||||
|
return Promise.reject(new Error("Invalid topic to pin"));
|
||||||
|
}
|
||||||
|
const tpin = Array.isArray(this.private && this.private.tpin) ? this.private.tpin : [];
|
||||||
|
const found = tpin.includes(topic);
|
||||||
|
if (pin && found || !pin && !found) {
|
||||||
|
return Promise.resolve(tpin);
|
||||||
|
}
|
||||||
|
if (pin) {
|
||||||
|
tpin.unshift(topic);
|
||||||
|
} else {
|
||||||
|
tpin.splice(tpin.indexOf(topic), 1);
|
||||||
|
}
|
||||||
|
return this.setMeta({
|
||||||
|
desc: {
|
||||||
|
private: {
|
||||||
|
tpin: tpin.length > 0 ? tpin : _config_js__WEBPACK_IMPORTED_MODULE_1__.DEL_CHAR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pinnedTopicRank(topic) {
|
||||||
|
if (!this.private || !this.private.tpin) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const idx = this.private.tpin.indexOf(topic);
|
||||||
|
return idx < 0 ? 0 : this.private.tpin.length - idx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@@ -4315,6 +4347,12 @@ class Topic {
|
|||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
pinTopic(topic, pin) {
|
||||||
|
return Promise.reject(new Error("Pinning topics is not supported here"));
|
||||||
|
}
|
||||||
|
pinnedTopicRank(topic) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
delMessages(ranges, hard) {
|
delMessages(ranges, hard) {
|
||||||
if (!this._attached) {
|
if (!this._attached) {
|
||||||
return Promise.reject(new Error("Cannot delete messages in inactive topic"));
|
return Promise.reject(new Error("Cannot delete messages in inactive topic"));
|
||||||
@@ -5006,10 +5044,14 @@ class Topic {
|
|||||||
acs: sub.acs
|
acs: sub.acs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!this._users[sub.user]) {
|
||||||
|
this.subcnt++;
|
||||||
|
}
|
||||||
user = this._updateCachedUser(sub.user, sub);
|
user = this._updateCachedUser(sub.user, sub);
|
||||||
} else {
|
} else {
|
||||||
delete this._users[sub.user];
|
delete this._users[sub.user];
|
||||||
user = sub;
|
user = sub;
|
||||||
|
this.subcnt--;
|
||||||
}
|
}
|
||||||
if (this.onMetaSub) {
|
if (this.onMetaSub) {
|
||||||
this.onMetaSub(user);
|
this.onMetaSub(user);
|
||||||
@@ -5204,7 +5246,7 @@ function rfc3339DateString(d) {
|
|||||||
const millis = d.getUTCMilliseconds();
|
const millis = d.getUTCMilliseconds();
|
||||||
return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + (millis ? '.' + pad(millis, 3) : '') + 'Z';
|
return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + (millis ? '.' + pad(millis, 3) : '') + 'Z';
|
||||||
}
|
}
|
||||||
function mergeObj(dst, src, ignore) {
|
function mergeObj(dst, src) {
|
||||||
if (typeof src != 'object') {
|
if (typeof src != 'object') {
|
||||||
if (src === undefined) {
|
if (src === undefined) {
|
||||||
return dst;
|
return dst;
|
||||||
@@ -5215,7 +5257,7 @@ function mergeObj(dst, src, ignore) {
|
|||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
if (src === null) {
|
if (src === null) {
|
||||||
return src;
|
return dst;
|
||||||
}
|
}
|
||||||
if (src instanceof Date && !isNaN(src)) {
|
if (src instanceof Date && !isNaN(src)) {
|
||||||
return !dst || !(dst instanceof Date) || isNaN(dst) || dst < src ? src : dst;
|
return !dst || !(dst instanceof Date) || isNaN(dst) || dst < src ? src : dst;
|
||||||
@@ -5230,16 +5272,18 @@ function mergeObj(dst, src, ignore) {
|
|||||||
dst = src.constructor();
|
dst = src.constructor();
|
||||||
}
|
}
|
||||||
for (let prop in src) {
|
for (let prop in src) {
|
||||||
if (src.hasOwnProperty(prop) && (!ignore || !ignore[prop]) && prop != '_noForwarding') {
|
if (src.hasOwnProperty(prop) && prop != '_noForwarding') {
|
||||||
try {
|
try {
|
||||||
dst[prop] = mergeObj(dst[prop], src[prop]);
|
dst[prop] = mergeObj(dst[prop], src[prop]);
|
||||||
} catch (err) {}
|
} catch (err) {
|
||||||
|
console.warn("Error merging property:", prop, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
function mergeToCache(cache, key, newval, ignore) {
|
function mergeToCache(cache, key, newval) {
|
||||||
cache[key] = mergeObj(cache[key], newval, ignore);
|
cache[key] = mergeObj(cache[key], newval);
|
||||||
return cache[key];
|
return cache[key];
|
||||||
}
|
}
|
||||||
function simplify(obj) {
|
function simplify(obj) {
|
||||||
@@ -5385,7 +5429,7 @@ function clipInRange(src, clip) {
|
|||||||
hi: Math.min(src.hi, clip.hi)
|
hi: Math.min(src.hi, clip.hi)
|
||||||
};
|
};
|
||||||
// removed by dead control flow
|
// removed by dead control flow
|
||||||
{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@@ -5400,7 +5444,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||||
/* harmony export */ PACKAGE_VERSION: () => (/* binding */ PACKAGE_VERSION)
|
/* harmony export */ PACKAGE_VERSION: () => (/* binding */ PACKAGE_VERSION)
|
||||||
/* harmony export */ });
|
/* harmony export */ });
|
||||||
const PACKAGE_VERSION = "0.24.4";
|
const PACKAGE_VERSION = "0.25.0-beta1";
|
||||||
|
|
||||||
/***/ })
|
/***/ })
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user