Merge branch 'develop' into travis/files/visual
This commit is contained in:
commit
60e3a8ac77
20 changed files with 324 additions and 122 deletions
|
@ -28,7 +28,7 @@ Platform Targets:
|
||||||
* WebRTC features (VoIP and Video calling) are only available in Chrome & Firefox.
|
* WebRTC features (VoIP and Video calling) are only available in Chrome & Firefox.
|
||||||
* Mobile Web is not currently a target platform - instead please use the native
|
* Mobile Web is not currently a target platform - instead please use the native
|
||||||
iOS (https://github.com/matrix-org/matrix-ios-kit) and Android
|
iOS (https://github.com/matrix-org/matrix-ios-kit) and Android
|
||||||
(https://github.com/matrix-org/matrix-android-sdk) SDKs.
|
(https://github.com/matrix-org/matrix-android-sdk2) SDKs.
|
||||||
|
|
||||||
All code lands on the `develop` branch - `master` is only used for stable releases.
|
All code lands on the `develop` branch - `master` is only used for stable releases.
|
||||||
**Please file PRs against `develop`!!**
|
**Please file PRs against `develop`!!**
|
||||||
|
|
|
@ -101,7 +101,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_BaseAvatar {
|
.mx_BaseAvatar {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin: 5px 16px 5px 5px;
|
margin: auto 16px auto 5px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,31 +160,32 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_errorText {
|
|
||||||
font-weight: $font-semi-bold;
|
|
||||||
font-size: $font-12px;
|
|
||||||
line-height: $font-15px;
|
|
||||||
color: $notice-primary-color;
|
|
||||||
margin-bottom: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpace {
|
.mx_AddExistingToSpace {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_footer {
|
.mx_AddExistingToSpaceDialog_footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 32px;
|
margin-top: 20px;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: $font-14px;
|
font-size: $font-12px;
|
||||||
line-height: $font-15px;
|
line-height: $font-15px;
|
||||||
font-weight: $font-semi-bold;
|
color: $secondary-fg-color;
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_ProgressBar {
|
||||||
font-size: inherit;
|
height: 8px;
|
||||||
display: inline-block;
|
width: 100%;
|
||||||
|
|
||||||
|
@mixin ProgressBarBorderRadius 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpaceDialog_progressText {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
color: $primary-fg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
@ -192,8 +193,54 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpaceDialog_error {
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpaceDialog_errorHeading {
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-18px;
|
||||||
|
color: $notice-primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpaceDialog_errorCaption {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_primary {
|
||||||
|
padding: 8px 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpaceDialog_retryButton {
|
||||||
|
margin-left: 12px;
|
||||||
|
padding-left: 24px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: $primary-fg-color;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-image: url('$(res)/img/element-icons/retry.svg');
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
.mx_AccessibleButton_kind_link {
|
||||||
|
|
|
@ -21,7 +21,7 @@ progress.mx_ProgressBar {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
@mixin ProgressBarBorderRadius "6px";
|
@mixin ProgressBarBorderRadius 6px;
|
||||||
@mixin ProgressBarColour $progressbar-fg-color;
|
@mixin ProgressBarColour $progressbar-fg-color;
|
||||||
@mixin ProgressBarBgColour $progressbar-bg-color;
|
@mixin ProgressBarBgColour $progressbar-bg-color;
|
||||||
::-webkit-progress-value {
|
::-webkit-progress-value {
|
||||||
|
|
|
@ -39,14 +39,14 @@ limitations under the License.
|
||||||
width: 14px; // w&h are size of icon
|
width: 14px; // w&h are size of icon
|
||||||
height: 18px;
|
height: 18px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 7px; // distance from left edge of waveform container (container has some margin too)
|
margin-right: 11px; // distance from left edge of waveform container (container has some margin too)
|
||||||
background-color: $voice-record-icon-color;
|
background-color: $voice-record-icon-color;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-image: url('$(res)/img/element-icons/trashcan.svg');
|
mask-image: url('$(res)/img/element-icons/trashcan.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_VoiceMessagePrimaryContainer {
|
.mx_VoiceRecordComposerTile_recording.mx_VoiceMessagePrimaryContainer {
|
||||||
// Note: remaining class properties are in the PlayerContainer CSS.
|
// Note: remaining class properties are in the PlayerContainer CSS.
|
||||||
|
|
||||||
margin: 6px; // force the composer area to put a gutter around us
|
margin: 6px; // force the composer area to put a gutter around us
|
||||||
|
@ -55,7 +55,9 @@ limitations under the License.
|
||||||
position: relative; // important for the live circle
|
position: relative; // important for the live circle
|
||||||
|
|
||||||
&.mx_VoiceRecordComposerTile_recording {
|
&.mx_VoiceRecordComposerTile_recording {
|
||||||
padding-left: 16px; // +10px for the live circle, +6px for regular padding
|
// We are putting the circle in this padding, so we need +10px from the regular
|
||||||
|
// padding on the left side.
|
||||||
|
padding-left: 22px;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
animation: recording-pulse 2s infinite;
|
animation: recording-pulse 2s infinite;
|
||||||
|
@ -65,8 +67,8 @@ limitations under the License.
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 8px;
|
left: 12px; // 12px from the left edge for container padding
|
||||||
top: 16px; // vertically center
|
top: 18px; // vertically center (middle align with clock)
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,3 +22,34 @@ limitations under the License.
|
||||||
.mx_HelpUserSettingsTab span.mx_AccessibleButton {
|
.mx_HelpUserSettingsTab span.mx_AccessibleButton {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_HelpUserSettingsTab code {
|
||||||
|
word-break: break-all;
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_HelpUserSettingsTab_accessToken {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: solid 1px $light-fg-color;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_HelpUserSettingsTab_accessToken_copy {
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 20px;
|
||||||
|
display: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_HelpUserSettingsTab_accessToken_copy > div {
|
||||||
|
mask-image: url($copy-button-url);
|
||||||
|
background-color: $message-action-bar-fg-color;
|
||||||
|
margin-left: 5px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
|
@ -19,8 +19,9 @@ limitations under the License.
|
||||||
|
|
||||||
// Container for live recording and playback controls
|
// Container for live recording and playback controls
|
||||||
.mx_VoiceMessagePrimaryContainer {
|
.mx_VoiceMessagePrimaryContainer {
|
||||||
padding: 6px; // makes us 4px taller than the send/stop button
|
// 7px top and bottom for visual design. 12px left & right, but the waveform (right)
|
||||||
padding-right: 5px; // there's 1px from the waveform itself, so account for that
|
// has a 1px padding on it that we want to account for.
|
||||||
|
padding: 7px 12px 7px 11px;
|
||||||
background-color: $voice-record-waveform-bg-color;
|
background-color: $voice-record-waveform-bg-color;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
|
@ -30,11 +31,9 @@ limitations under the License.
|
||||||
|
|
||||||
color: $voice-record-waveform-fg-color;
|
color: $voice-record-waveform-fg-color;
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
|
||||||
.mx_Waveform {
|
.mx_Waveform {
|
||||||
// We want the bars to be 2px shorter than the play/pause button in the waveform control
|
|
||||||
height: 28px; // default is 30px, so we're subtracting the 2px border off the bars
|
|
||||||
|
|
||||||
.mx_Waveform_bar {
|
.mx_Waveform_bar {
|
||||||
background-color: $voice-record-waveform-incomplete-fg-color;
|
background-color: $voice-record-waveform-incomplete-fg-color;
|
||||||
|
|
||||||
|
@ -47,8 +46,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Clock {
|
.mx_Clock {
|
||||||
padding-right: 4px; // isolate from waveform
|
width: 42px; // we're not using a monospace font, so fake it
|
||||||
padding-left: 8px; // isolate from live circle
|
padding-right: 6px; // with the fixed width this ends up as a visual 8px most of the time, as intended.
|
||||||
width: 40px; // we're not using a monospace font, so fake it
|
padding-left: 8px; // isolate from recording circle / play control
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ $header-panel-text-primary-color: #B9BEC6;
|
||||||
$header-panel-text-secondary-color: #c8c8cd;
|
$header-panel-text-secondary-color: #c8c8cd;
|
||||||
$text-primary-color: #ffffff;
|
$text-primary-color: #ffffff;
|
||||||
$text-secondary-color: #B9BEC6;
|
$text-secondary-color: #B9BEC6;
|
||||||
|
$quaternary-fg-color: #6F7882;
|
||||||
$search-bg-color: #181b21;
|
$search-bg-color: #181b21;
|
||||||
$search-placeholder-color: #61708b;
|
$search-placeholder-color: #61708b;
|
||||||
$room-highlight-color: #343a46;
|
$room-highlight-color: #343a46;
|
||||||
|
@ -42,13 +43,13 @@ $preview-bar-bg-color: $header-panel-bg-color;
|
||||||
$groupFilterPanel-bg-color: rgba(38, 39, 43, 0.82);
|
$groupFilterPanel-bg-color: rgba(38, 39, 43, 0.82);
|
||||||
$inverted-bg-color: $base-color;
|
$inverted-bg-color: $base-color;
|
||||||
|
|
||||||
$voice-record-stop-border-color: #6F7882; // "Quarterly"
|
$voice-record-stop-border-color: $quaternary-fg-color;
|
||||||
$voice-record-waveform-bg-color: #394049; // "Dark Tile"
|
$voice-record-waveform-bg-color: #394049; // "Dark Tile"
|
||||||
$voice-record-waveform-fg-color: $tertiary-fg-color;
|
$voice-record-waveform-fg-color: $secondary-fg-color;
|
||||||
$voice-record-waveform-incomplete-fg-color: #5b646d;
|
$voice-record-waveform-incomplete-fg-color: $quaternary-fg-color;
|
||||||
$voice-record-icon-color: $tertiary-fg-color;
|
$voice-record-icon-color: $quaternary-fg-color;
|
||||||
$voice-playback-button-bg-color: $tertiary-fg-color;
|
$voice-playback-button-bg-color: $tertiary-fg-color;
|
||||||
$voice-playback-button-fg-color: $bg-color;
|
$voice-playback-button-fg-color: #21262C; // "Separator"
|
||||||
|
|
||||||
// used by AddressSelector
|
// used by AddressSelector
|
||||||
$selected-color: $room-highlight-color;
|
$selected-color: $room-highlight-color;
|
||||||
|
|
|
@ -127,11 +127,11 @@ $groupFilterPanel-divider-color: $roomlist-header-color;
|
||||||
// See non-legacy dark for variable information
|
// See non-legacy dark for variable information
|
||||||
$voice-record-stop-border-color: #6F7882;
|
$voice-record-stop-border-color: #6F7882;
|
||||||
$voice-record-waveform-bg-color: #394049;
|
$voice-record-waveform-bg-color: #394049;
|
||||||
$voice-record-waveform-fg-color: $tertiary-fg-color;
|
$voice-record-waveform-fg-color: $secondary-fg-color;
|
||||||
$voice-record-waveform-incomplete-fg-color: #5b646d;
|
$voice-record-waveform-incomplete-fg-color: #6F7882;
|
||||||
$voice-record-icon-color: $tertiary-fg-color;
|
$voice-record-icon-color: #6F7882;
|
||||||
$voice-playback-button-bg-color: $tertiary-fg-color;
|
$voice-playback-button-bg-color: $tertiary-fg-color;
|
||||||
$voice-playback-button-fg-color: $bg-color;
|
$voice-playback-button-fg-color: #21262C;
|
||||||
|
|
||||||
$roomtile-preview-color: #9e9e9e;
|
$roomtile-preview-color: #9e9e9e;
|
||||||
$roomtile-default-badge-bg-color: #61708b;
|
$roomtile-default-badge-bg-color: #61708b;
|
||||||
|
|
|
@ -192,15 +192,15 @@ $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%)
|
||||||
$groupFilterPanel-divider-color: $roomlist-header-color;
|
$groupFilterPanel-divider-color: $roomlist-header-color;
|
||||||
|
|
||||||
// See non-legacy _light for variable information
|
// See non-legacy _light for variable information
|
||||||
$voice-record-stop-border-color: #E3E8F0;
|
|
||||||
$voice-record-stop-symbol-color: #ff4b55;
|
$voice-record-stop-symbol-color: #ff4b55;
|
||||||
$voice-record-waveform-bg-color: #E3E8F0;
|
|
||||||
$voice-record-waveform-fg-color: $muted-fg-color;
|
|
||||||
$voice-record-waveform-incomplete-fg-color: #C1C6CD;
|
|
||||||
$voice-record-live-circle-color: #ff4b55;
|
$voice-record-live-circle-color: #ff4b55;
|
||||||
$voice-record-icon-color: $muted-fg-color;
|
$voice-record-stop-border-color: #E3E8F0;
|
||||||
|
$voice-record-waveform-bg-color: #E3E8F0;
|
||||||
|
$voice-record-waveform-fg-color: $secondary-fg-color;
|
||||||
|
$voice-record-waveform-incomplete-fg-color: #C1C6CD;
|
||||||
|
$voice-record-icon-color: $tertiary-fg-color;
|
||||||
$voice-playback-button-bg-color: $primary-bg-color;
|
$voice-playback-button-bg-color: $primary-bg-color;
|
||||||
$voice-playback-button-fg-color: $muted-fg-color;
|
$voice-playback-button-fg-color: $secondary-fg-color;
|
||||||
|
|
||||||
$fileinfo-bg-color: $voice-record-waveform-bg-color;
|
$fileinfo-bg-color: $voice-record-waveform-bg-color;
|
||||||
$fileinfo-fg-color: $secondary-fg-color;
|
$fileinfo-fg-color: $secondary-fg-color;
|
||||||
|
|
|
@ -21,6 +21,7 @@ $notice-primary-bg-color: rgba(255, 75, 85, 0.16);
|
||||||
$primary-fg-color: #2e2f32;
|
$primary-fg-color: #2e2f32;
|
||||||
$secondary-fg-color: #737D8C;
|
$secondary-fg-color: #737D8C;
|
||||||
$tertiary-fg-color: #8D99A5;
|
$tertiary-fg-color: #8D99A5;
|
||||||
|
$quaternary-fg-color: #C1C6CD;
|
||||||
$header-panel-bg-color: #f3f8fd;
|
$header-panel-bg-color: #f3f8fd;
|
||||||
|
|
||||||
// typical text (dark-on-white in light skin)
|
// typical text (dark-on-white in light skin)
|
||||||
|
@ -182,15 +183,18 @@ $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%)
|
||||||
|
|
||||||
$groupFilterPanel-divider-color: $roomlist-header-color;
|
$groupFilterPanel-divider-color: $roomlist-header-color;
|
||||||
|
|
||||||
$voice-record-stop-border-color: #E3E8F0;
|
// These two don't change between themes. They are the $warning-color, but we don't
|
||||||
$voice-record-stop-symbol-color: #ff4b55; // $warning-color, but without letting people change it in themes
|
// want custom themes to affect them by accident.
|
||||||
$voice-record-waveform-bg-color: #E3E8F0;
|
$voice-record-stop-symbol-color: #ff4b55;
|
||||||
$voice-record-waveform-fg-color: $muted-fg-color;
|
$voice-record-live-circle-color: #ff4b55;
|
||||||
$voice-record-waveform-incomplete-fg-color: #C1C6CD;
|
|
||||||
$voice-record-live-circle-color: #ff4b55; // $warning-color, but without letting people change it in themes
|
$voice-record-stop-border-color: #E3E8F0; // "Separator"
|
||||||
$voice-record-icon-color: $muted-fg-color;
|
$voice-record-waveform-bg-color: #E3E8F0; // "Separator"
|
||||||
|
$voice-record-waveform-fg-color: $secondary-fg-color;
|
||||||
|
$voice-record-waveform-incomplete-fg-color: $quaternary-fg-color;
|
||||||
|
$voice-record-icon-color: $tertiary-fg-color;
|
||||||
$voice-playback-button-bg-color: $primary-bg-color;
|
$voice-playback-button-bg-color: $primary-bg-color;
|
||||||
$voice-playback-button-fg-color: $muted-fg-color;
|
$voice-playback-button-fg-color: $secondary-fg-color;
|
||||||
|
|
||||||
$roomtile-preview-color: $secondary-fg-color;
|
$roomtile-preview-color: $secondary-fg-color;
|
||||||
$roomtile-default-badge-bg-color: #61708b;
|
$roomtile-default-badge-bg-color: #61708b;
|
||||||
|
|
|
@ -52,7 +52,7 @@ import {useStateToggle} from "../../hooks/useStateToggle";
|
||||||
import SpaceStore from "../../stores/SpaceStore";
|
import SpaceStore from "../../stores/SpaceStore";
|
||||||
import FacePile from "../views/elements/FacePile";
|
import FacePile from "../views/elements/FacePile";
|
||||||
import {AddExistingToSpace} from "../views/dialogs/AddExistingToSpaceDialog";
|
import {AddExistingToSpace} from "../views/dialogs/AddExistingToSpaceDialog";
|
||||||
import {allSettled} from "../../utils/promise";
|
import {sleep} from "../../utils/promise";
|
||||||
import {calculateRoomVia} from "../../utils/permalinks/Permalinks";
|
import {calculateRoomVia} from "../../utils/permalinks/Permalinks";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -389,15 +389,24 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
||||||
let buttonLabel = _t("Skip for now");
|
let buttonLabel = _t("Skip for now");
|
||||||
if (selectedToAdd.size > 0) {
|
if (selectedToAdd.size > 0) {
|
||||||
onClick = async () => {
|
onClick = async () => {
|
||||||
// TODO rate limiting
|
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
||||||
|
for (const room of selectedToAdd) {
|
||||||
|
const via = calculateRoomVia(room);
|
||||||
try {
|
try {
|
||||||
await allSettled(Array.from(selectedToAdd).map((room) =>
|
await SpaceStore.instance.addRoomToSpace(space, room.roomId, via).catch(async e => {
|
||||||
SpaceStore.instance.addRoomToSpace(space, room.roomId, calculateRoomVia(room))));
|
if (e.errcode === "M_LIMIT_EXCEEDED") {
|
||||||
onFinished(true);
|
await sleep(e.data.retry_after_ms);
|
||||||
|
return SpaceStore.instance.addRoomToSpace(space, room.roomId, via); // retry
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to add rooms to space", e);
|
console.error("Failed to add rooms to space", e);
|
||||||
setError(_t("Failed to add rooms to space"));
|
setError(_t("Failed to add rooms to space"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,12 +29,13 @@ import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
import {getDisplayAliasForRoom} from "../../../Rooms";
|
import {getDisplayAliasForRoom} from "../../../Rooms";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import {allSettled} from "../../../utils/promise";
|
import {sleep} from "../../../utils/promise";
|
||||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||||
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
|
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
|
||||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||||
|
import ProgressBar from "../elements/ProgressBar";
|
||||||
|
|
||||||
interface IProps extends IDialogProps {
|
interface IProps extends IDialogProps {
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
|
@ -46,7 +47,11 @@ const Entry = ({ room, checked, onChange }) => {
|
||||||
return <label className="mx_AddExistingToSpace_entry">
|
return <label className="mx_AddExistingToSpace_entry">
|
||||||
<RoomAvatar room={room} height={32} width={32} />
|
<RoomAvatar room={room} height={32} width={32} />
|
||||||
<span className="mx_AddExistingToSpace_entry_name">{ room.name }</span>
|
<span className="mx_AddExistingToSpace_entry_name">{ room.name }</span>
|
||||||
<StyledCheckbox onChange={(e) => onChange(e.target.checked)} checked={checked} />
|
<StyledCheckbox
|
||||||
|
onChange={onChange ? (e) => onChange(e.target.checked) : null}
|
||||||
|
checked={checked}
|
||||||
|
disabled={!onChange}
|
||||||
|
/>
|
||||||
</label>;
|
</label>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,9 +109,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
|
||||||
key={room.roomId}
|
key={room.roomId}
|
||||||
room={room}
|
room={room}
|
||||||
checked={selected.has(room)}
|
checked={selected.has(room)}
|
||||||
onChange={(checked) => {
|
onChange={onChange ? (checked) => {
|
||||||
onChange(checked, room);
|
onChange(checked, room);
|
||||||
}}
|
} : null}
|
||||||
/>;
|
/>;
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</div>
|
||||||
|
@ -120,9 +125,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
|
||||||
key={space.roomId}
|
key={space.roomId}
|
||||||
room={space}
|
room={space}
|
||||||
checked={selected.has(space)}
|
checked={selected.has(space)}
|
||||||
onChange={(checked) => {
|
onChange={onChange ? (checked) => {
|
||||||
onChange(checked, space);
|
onChange(checked, space);
|
||||||
}}
|
} : null}
|
||||||
/>;
|
/>;
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,9 +141,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
|
||||||
key={room.roomId}
|
key={room.roomId}
|
||||||
room={room}
|
room={room}
|
||||||
checked={selected.has(room)}
|
checked={selected.has(room)}
|
||||||
onChange={(checked) => {
|
onChange={onChange ? (checked) => {
|
||||||
onChange(checked, room);
|
onChange(checked, room);
|
||||||
}}
|
} : null}
|
||||||
/>;
|
/>;
|
||||||
}) }
|
}) }
|
||||||
</div>
|
</div>
|
||||||
|
@ -156,8 +161,8 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
||||||
const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
|
const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
|
||||||
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
|
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
|
||||||
|
|
||||||
const [busy, setBusy] = useState(false);
|
const [progress, setProgress] = useState<number>(null);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState<Error>(null);
|
||||||
|
|
||||||
let spaceOptionSection;
|
let spaceOptionSection;
|
||||||
if (existingSubspaces.length > 0) {
|
if (existingSubspaces.length > 0) {
|
||||||
|
@ -197,6 +202,82 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
|
|
||||||
|
const addRooms = async () => {
|
||||||
|
setError(null);
|
||||||
|
setProgress(0);
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
for (const room of selectedToAdd) {
|
||||||
|
const via = calculateRoomVia(room);
|
||||||
|
try {
|
||||||
|
await SpaceStore.instance.addRoomToSpace(space, room.roomId, via).catch(async e => {
|
||||||
|
if (e.errcode === "M_LIMIT_EXCEEDED") {
|
||||||
|
await sleep(e.data.retry_after_ms);
|
||||||
|
return SpaceStore.instance.addRoomToSpace(space, room.roomId, via); // retry
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
setProgress(i => i + 1);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to add rooms to space", e);
|
||||||
|
setError(error = e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
onFinished(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const busy = progress !== null;
|
||||||
|
|
||||||
|
let footer;
|
||||||
|
if (error) {
|
||||||
|
footer = <>
|
||||||
|
<img
|
||||||
|
src={require("../../../../res/img/element-icons/warning-badge.svg")}
|
||||||
|
height="24"
|
||||||
|
width="24"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span className="mx_AddExistingToSpaceDialog_error">
|
||||||
|
<div className="mx_AddExistingToSpaceDialog_errorHeading">{ _t("Not all selected were added") }</div>
|
||||||
|
<div className="mx_AddExistingToSpaceDialog_errorCaption">{ _t("Try again") }</div>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<AccessibleButton className="mx_AddExistingToSpaceDialog_retryButton" onClick={addRooms}>
|
||||||
|
{ _t("Retry") }
|
||||||
|
</AccessibleButton>
|
||||||
|
</>;
|
||||||
|
} else if (busy) {
|
||||||
|
footer = <span>
|
||||||
|
<ProgressBar value={progress} max={selectedToAdd.size} />
|
||||||
|
<div className="mx_AddExistingToSpaceDialog_progressText">
|
||||||
|
{ _t("Adding rooms... (%(progress)s out of %(count)s)", {
|
||||||
|
count: selectedToAdd.size,
|
||||||
|
progress,
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
</span>;
|
||||||
|
} else {
|
||||||
|
footer = <>
|
||||||
|
<span>
|
||||||
|
<div>{ _t("Want to add a new room instead?") }</div>
|
||||||
|
<AccessibleButton onClick={() => onCreateRoomClick(cli, space)} kind="link">
|
||||||
|
{ _t("Create a new room") }
|
||||||
|
</AccessibleButton>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<AccessibleButton kind="primary" disabled={selectedToAdd.size < 1} onClick={addRooms}>
|
||||||
|
{ _t("Add") }
|
||||||
|
</AccessibleButton>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
return <BaseDialog
|
return <BaseDialog
|
||||||
title={title}
|
title={title}
|
||||||
className="mx_AddExistingToSpaceDialog"
|
className="mx_AddExistingToSpaceDialog"
|
||||||
|
@ -204,50 +285,23 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
||||||
onFinished={onFinished}
|
onFinished={onFinished}
|
||||||
fixedWidth={false}
|
fixedWidth={false}
|
||||||
>
|
>
|
||||||
{ error && <div className="mx_AddExistingToSpaceDialog_errorText">{ error }</div> }
|
|
||||||
|
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<AddExistingToSpace
|
<AddExistingToSpace
|
||||||
space={space}
|
space={space}
|
||||||
selected={selectedToAdd}
|
selected={selectedToAdd}
|
||||||
onChange={(checked, room) => {
|
onChange={!busy && !error ? (checked, room) => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
selectedToAdd.add(room);
|
selectedToAdd.add(room);
|
||||||
} else {
|
} else {
|
||||||
selectedToAdd.delete(room);
|
selectedToAdd.delete(room);
|
||||||
}
|
}
|
||||||
setSelectedToAdd(new Set(selectedToAdd));
|
setSelectedToAdd(new Set(selectedToAdd));
|
||||||
}}
|
} : null}
|
||||||
/>
|
/>
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
|
|
||||||
<div className="mx_AddExistingToSpaceDialog_footer">
|
<div className="mx_AddExistingToSpaceDialog_footer">
|
||||||
<span>
|
{ footer }
|
||||||
<div>{ _t("Don't want to add an existing room?") }</div>
|
|
||||||
<AccessibleButton onClick={() => onCreateRoomClick(cli, space)} kind="link">
|
|
||||||
{ _t("Create a new room") }
|
|
||||||
</AccessibleButton>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<AccessibleButton
|
|
||||||
kind="primary"
|
|
||||||
disabled={busy || selectedToAdd.size < 1}
|
|
||||||
onClick={async () => {
|
|
||||||
// TODO rate limiting
|
|
||||||
setBusy(true);
|
|
||||||
try {
|
|
||||||
await allSettled(Array.from(selectedToAdd).map((room) =>
|
|
||||||
SpaceStore.instance.addRoomToSpace(space, room.roomId, calculateRoomVia(room))));
|
|
||||||
onFinished(true);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Failed to add rooms to space", e);
|
|
||||||
setError(_t("Failed to add rooms to space"));
|
|
||||||
}
|
|
||||||
setBusy(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ busy ? _t("Adding...") : _t("Add") }
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>;
|
</BaseDialog>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -114,6 +114,8 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.focusLock.current.removeEventListener('wheel', this.onWheel);
|
this.focusLock.current.removeEventListener('wheel', this.onWheel);
|
||||||
|
window.removeEventListener("resize", this.calculateZoom);
|
||||||
|
this.image.current.removeEventListener("load", this.calculateZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateZoom = () => {
|
private calculateZoom = () => {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import React from 'react';
|
||||||
import {_t, getCurrentLanguage} from "../../../../../languageHandler";
|
import {_t, getCurrentLanguage} from "../../../../../languageHandler";
|
||||||
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
||||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||||
|
import AccessibleTooltipButton from '../../../elements/AccessibleTooltipButton';
|
||||||
import SdkConfig from "../../../../../SdkConfig";
|
import SdkConfig from "../../../../../SdkConfig";
|
||||||
import createRoom from "../../../../../createRoom";
|
import createRoom from "../../../../../createRoom";
|
||||||
import Modal from "../../../../../Modal";
|
import Modal from "../../../../../Modal";
|
||||||
|
@ -26,6 +27,9 @@ import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
|
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
|
||||||
import UpdateCheckButton from "../../UpdateCheckButton";
|
import UpdateCheckButton from "../../UpdateCheckButton";
|
||||||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||||
|
import { copyPlaintext } from "../../../../../utils/strings";
|
||||||
|
import * as ContextMenu from "../../../../structures/ContextMenu";
|
||||||
|
import { toRightOf } from "../../../../structures/ContextMenu";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeSettingsFn: () => {};
|
closeSettingsFn: () => {};
|
||||||
|
@ -38,6 +42,8 @@ interface IState {
|
||||||
|
|
||||||
@replaceableComponent("views.settings.tabs.user.HelpUserSettingsTab")
|
@replaceableComponent("views.settings.tabs.user.HelpUserSettingsTab")
|
||||||
export default class HelpUserSettingsTab extends React.Component<IProps, IState> {
|
export default class HelpUserSettingsTab extends React.Component<IProps, IState> {
|
||||||
|
protected closeCopiedTooltip: () => void;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -56,6 +62,12 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
// if the Copied tooltip is open then get rid of it, there are ways to close the modal which wouldn't close
|
||||||
|
// the tooltip otherwise, such as pressing Escape
|
||||||
|
if (this.closeCopiedTooltip) this.closeCopiedTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
private onClearCacheAndReload = (e) => {
|
private onClearCacheAndReload = (e) => {
|
||||||
if (!PlatformPeg.get()) return;
|
if (!PlatformPeg.get()) return;
|
||||||
|
|
||||||
|
@ -153,6 +165,20 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAccessTokenCopyClick = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = e.target; // copy target before we go async and React throws it away
|
||||||
|
|
||||||
|
const successful = await copyPlaintext(MatrixClientPeg.get().getAccessToken());
|
||||||
|
const buttonRect = target.getBoundingClientRect();
|
||||||
|
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
|
||||||
|
const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||||
|
...toRightOf(buttonRect, 2),
|
||||||
|
message: successful ? _t('Copied!') : _t('Failed to copy'),
|
||||||
|
});
|
||||||
|
this.closeCopiedTooltip = target.onmouseleave = close;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
|
@ -269,12 +295,20 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
||||||
<div className='mx_SettingsTab_subsectionText'>
|
<div className='mx_SettingsTab_subsectionText'>
|
||||||
{_t("Homeserver is")} <code>{MatrixClientPeg.get().getHomeserverUrl()}</code><br />
|
{_t("Homeserver is")} <code>{MatrixClientPeg.get().getHomeserverUrl()}</code><br />
|
||||||
{_t("Identity Server is")} <code>{MatrixClientPeg.get().getIdentityServerUrl()}</code><br />
|
{_t("Identity Server is")} <code>{MatrixClientPeg.get().getIdentityServerUrl()}</code><br />
|
||||||
{_t("Access Token:") + ' '}
|
<br />
|
||||||
<AccessibleButton element="span" onClick={this.showSpoiler}
|
<details>
|
||||||
data-spoiler={MatrixClientPeg.get().getAccessToken()}
|
<summary>{_t("Access Token")}</summary><br />
|
||||||
>
|
<b>{_t("Your access token gives full access to your account."
|
||||||
<{ _t("click to reveal") }>
|
+ " Do not share it with anyone." )}</b>
|
||||||
</AccessibleButton>
|
<div className="mx_HelpUserSettingsTab_accessToken">
|
||||||
|
<code>{MatrixClientPeg.get().getAccessToken()}</code>
|
||||||
|
<AccessibleTooltipButton
|
||||||
|
title={_t("Copy")}
|
||||||
|
onClick={this.onAccessTokenCopyClick}
|
||||||
|
className="mx_HelpUserSettingsTab_accessToken_copy"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</details><br />
|
||||||
<div className='mx_HelpUserSettingsTab_debugButton'>
|
<div className='mx_HelpUserSettingsTab_debugButton'>
|
||||||
<AccessibleButton onClick={this.onClearCacheAndReload} kind='danger'>
|
<AccessibleButton onClick={this.onClearCacheAndReload} kind='danger'>
|
||||||
{_t("Clear cache and reload")}
|
{_t("Clear cache and reload")}
|
||||||
|
|
|
@ -15,12 +15,11 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {IRecordingUpdate, VoiceRecording} from "../../../voice/VoiceRecording";
|
import {IRecordingUpdate, RECORDING_PLAYBACK_SAMPLES, VoiceRecording} from "../../../voice/VoiceRecording";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
import {arrayFastResample, arraySeed} from "../../../utils/arrays";
|
import {arrayFastResample, arraySeed} from "../../../utils/arrays";
|
||||||
import {percentageOf} from "../../../utils/numbers";
|
import {percentageOf} from "../../../utils/numbers";
|
||||||
import Waveform from "./Waveform";
|
import Waveform from "./Waveform";
|
||||||
import {PLAYBACK_WAVEFORM_SAMPLES} from "../../../voice/Playback";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
recorder: VoiceRecording;
|
recorder: VoiceRecording;
|
||||||
|
@ -38,14 +37,14 @@ export default class LiveRecordingWaveform extends React.PureComponent<IProps, I
|
||||||
public constructor(props) {
|
public constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {heights: arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES)};
|
this.state = {heights: arraySeed(0, RECORDING_PLAYBACK_SAMPLES)};
|
||||||
this.props.recorder.liveData.onUpdate(this.onRecordingUpdate);
|
this.props.recorder.liveData.onUpdate(this.onRecordingUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRecordingUpdate = (update: IRecordingUpdate) => {
|
private onRecordingUpdate = (update: IRecordingUpdate) => {
|
||||||
// The waveform and the downsample target are pretty close, so we should be fine to
|
// The waveform and the downsample target are pretty close, so we should be fine to
|
||||||
// do this, despite the docs on arrayFastResample.
|
// do this, despite the docs on arrayFastResample.
|
||||||
const bars = arrayFastResample(Array.from(update.waveform), PLAYBACK_WAVEFORM_SAMPLES);
|
const bars = arrayFastResample(Array.from(update.waveform), RECORDING_PLAYBACK_SAMPLES);
|
||||||
this.setState({
|
this.setState({
|
||||||
// The incoming data is between zero and one, but typically even screaming into a
|
// The incoming data is between zero and one, but typically even screaming into a
|
||||||
// microphone won't send you over 0.6, so we artificially adjust the gain for the
|
// microphone won't send you over 0.6, so we artificially adjust the gain for the
|
||||||
|
|
|
@ -833,7 +833,7 @@
|
||||||
"Match system theme": "Match system theme",
|
"Match system theme": "Match system theme",
|
||||||
"Use a system font": "Use a system font",
|
"Use a system font": "Use a system font",
|
||||||
"System font name": "System font name",
|
"System font name": "System font name",
|
||||||
"Allow Peer-to-Peer for 1:1 calls": "Allow Peer-to-Peer for 1:1 calls",
|
"Allow Peer-to-Peer for 1:1 calls (if you enable this, the other party might be able to see your IP address)": "Allow Peer-to-Peer for 1:1 calls (if you enable this, the other party might be able to see your IP address)",
|
||||||
"Send analytics data": "Send analytics data",
|
"Send analytics data": "Send analytics data",
|
||||||
"Never send encrypted messages to unverified sessions from this session": "Never send encrypted messages to unverified sessions from this session",
|
"Never send encrypted messages to unverified sessions from this session": "Never send encrypted messages to unverified sessions from this session",
|
||||||
"Never send encrypted messages to unverified sessions in this room from this session": "Never send encrypted messages to unverified sessions in this room from this session",
|
"Never send encrypted messages to unverified sessions in this room from this session": "Never send encrypted messages to unverified sessions in this room from this session",
|
||||||
|
@ -1252,8 +1252,9 @@
|
||||||
"olm version:": "olm version:",
|
"olm version:": "olm version:",
|
||||||
"Homeserver is": "Homeserver is",
|
"Homeserver is": "Homeserver is",
|
||||||
"Identity Server is": "Identity Server is",
|
"Identity Server is": "Identity Server is",
|
||||||
"Access Token:": "Access Token:",
|
"Access Token": "Access Token",
|
||||||
"click to reveal": "click to reveal",
|
"Your access token gives full access to your account. Do not share it with anyone.": "Your access token gives full access to your account. Do not share it with anyone.",
|
||||||
|
"Copy": "Copy",
|
||||||
"Clear cache and reload": "Clear cache and reload",
|
"Clear cache and reload": "Clear cache and reload",
|
||||||
"Labs": "Labs",
|
"Labs": "Labs",
|
||||||
"Customise your experience with experimental labs features. <a>Learn more</a>.": "Customise your experience with experimental labs features. <a>Learn more</a>.",
|
"Customise your experience with experimental labs features. <a>Learn more</a>.": "Customise your experience with experimental labs features. <a>Learn more</a>.",
|
||||||
|
@ -2033,10 +2034,11 @@
|
||||||
"Direct Messages": "Direct Messages",
|
"Direct Messages": "Direct Messages",
|
||||||
"Space selection": "Space selection",
|
"Space selection": "Space selection",
|
||||||
"Add existing rooms": "Add existing rooms",
|
"Add existing rooms": "Add existing rooms",
|
||||||
"Don't want to add an existing room?": "Don't want to add an existing room?",
|
"Not all selected were added": "Not all selected were added",
|
||||||
|
"Adding rooms... (%(progress)s out of %(count)s)|other": "Adding rooms... (%(progress)s out of %(count)s)",
|
||||||
|
"Adding rooms... (%(progress)s out of %(count)s)|one": "Adding room...",
|
||||||
|
"Want to add a new room instead?": "Want to add a new room instead?",
|
||||||
"Create a new room": "Create a new room",
|
"Create a new room": "Create a new room",
|
||||||
"Failed to add rooms to space": "Failed to add rooms to space",
|
|
||||||
"Adding...": "Adding...",
|
|
||||||
"Matrix ID": "Matrix ID",
|
"Matrix ID": "Matrix ID",
|
||||||
"Matrix Room ID": "Matrix Room ID",
|
"Matrix Room ID": "Matrix Room ID",
|
||||||
"email address": "email address",
|
"email address": "email address",
|
||||||
|
@ -2347,7 +2349,6 @@
|
||||||
"Share Community": "Share Community",
|
"Share Community": "Share Community",
|
||||||
"Share Room Message": "Share Room Message",
|
"Share Room Message": "Share Room Message",
|
||||||
"Link to selected message": "Link to selected message",
|
"Link to selected message": "Link to selected message",
|
||||||
"Copy": "Copy",
|
|
||||||
"Command Help": "Command Help",
|
"Command Help": "Command Help",
|
||||||
"Failed to save space settings.": "Failed to save space settings.",
|
"Failed to save space settings.": "Failed to save space settings.",
|
||||||
"Space settings": "Space settings",
|
"Space settings": "Space settings",
|
||||||
|
@ -2675,6 +2676,8 @@
|
||||||
"Failed to create initial space rooms": "Failed to create initial space rooms",
|
"Failed to create initial space rooms": "Failed to create initial space rooms",
|
||||||
"Skip for now": "Skip for now",
|
"Skip for now": "Skip for now",
|
||||||
"Creating rooms...": "Creating rooms...",
|
"Creating rooms...": "Creating rooms...",
|
||||||
|
"Failed to add rooms to space": "Failed to add rooms to space",
|
||||||
|
"Adding...": "Adding...",
|
||||||
"What do you want to organise?": "What do you want to organise?",
|
"What do you want to organise?": "What do you want to organise?",
|
||||||
"Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.": "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.",
|
"Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.": "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.",
|
||||||
"Share %(name)s": "Share %(name)s",
|
"Share %(name)s": "Share %(name)s",
|
||||||
|
|
|
@ -438,7 +438,10 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
},
|
},
|
||||||
"webRtcAllowPeerToPeer": {
|
"webRtcAllowPeerToPeer": {
|
||||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
||||||
displayName: _td('Allow Peer-to-Peer for 1:1 calls'),
|
displayName: _td(
|
||||||
|
"Allow Peer-to-Peer for 1:1 calls " +
|
||||||
|
"(if you enable this, the other party might be able to see your IP address)",
|
||||||
|
),
|
||||||
default: true,
|
default: true,
|
||||||
invertedSettingName: 'webRtcForceTURN',
|
invertedSettingName: 'webRtcForceTURN',
|
||||||
},
|
},
|
||||||
|
|
|
@ -413,7 +413,19 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
|
|
||||||
this.spaceFilteredRooms.forEach((roomIds, s) => {
|
this.spaceFilteredRooms.forEach((roomIds, s) => {
|
||||||
// Update NotificationStates
|
// Update NotificationStates
|
||||||
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => roomIds.has(room.roomId)));
|
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
|
||||||
|
if (roomIds.has(room.roomId)) {
|
||||||
|
// Don't aggregate notifications for DMs except in the Home Space
|
||||||
|
if (s !== HOME_SPACE) {
|
||||||
|
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|
||||||
|
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}, 100, {trailing: true, leading: true});
|
}, 100, {trailing: true, leading: true});
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export enum PlaybackState {
|
||||||
Playing = "playing", // active progress through timeline
|
Playing = "playing", // active progress through timeline
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PLAYBACK_WAVEFORM_SAMPLES = 35;
|
export const PLAYBACK_WAVEFORM_SAMPLES = 39;
|
||||||
const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
|
const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
|
||||||
|
|
||||||
export class Playback extends EventEmitter implements IDestroyable {
|
export class Playback extends EventEmitter implements IDestroyable {
|
||||||
|
|
|
@ -33,6 +33,8 @@ const BITRATE = 24000; // 24kbps is pretty high quality for our use case in opus
|
||||||
const TARGET_MAX_LENGTH = 120; // 2 minutes in seconds. Somewhat arbitrary, though longer == larger files.
|
const TARGET_MAX_LENGTH = 120; // 2 minutes in seconds. Somewhat arbitrary, though longer == larger files.
|
||||||
const TARGET_WARN_TIME_LEFT = 10; // 10 seconds, also somewhat arbitrary.
|
const TARGET_WARN_TIME_LEFT = 10; // 10 seconds, also somewhat arbitrary.
|
||||||
|
|
||||||
|
export const RECORDING_PLAYBACK_SAMPLES = 44;
|
||||||
|
|
||||||
export interface IRecordingUpdate {
|
export interface IRecordingUpdate {
|
||||||
waveform: number[]; // floating points between 0 (low) and 1 (high).
|
waveform: number[]; // floating points between 0 (low) and 1 (high).
|
||||||
timeSeconds: number; // float
|
timeSeconds: number; // float
|
||||||
|
|
Loading…
Reference in a new issue