mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-01 12:00:48 +00:00
Add some error tooltips in group calls.
This commit is contained in:
parent
5b0278847d
commit
9a812090a2
@ -2004,8 +2004,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_screen_share_stop" = "Stop Sharing";
|
||||
"lng_group_call_screen_title" = "Screen {index}";
|
||||
"lng_group_call_unmute_small" = "Unmute";
|
||||
"lng_group_call_you_are_live_small" = "Mute";
|
||||
"lng_group_call_force_muted_small" = "Muted";
|
||||
"lng_group_call_more" = "More";
|
||||
"lng_group_call_unmute" = "Unmute";
|
||||
"lng_group_call_unmute_sub" = "or hold spacebar to talk";
|
||||
@ -2014,7 +2012,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_force_muted_sub" = "You are in Listen Only mode";
|
||||
"lng_group_call_raise_hand_tip" = "Click if you want to speak";
|
||||
"lng_group_call_raised_hand" = "You asked to speak";
|
||||
"lng_group_call_raised_hand_small" = "Raised hand";
|
||||
"lng_group_call_raised_hand_sub" = "We let the speakers know";
|
||||
"lng_group_call_connecting" = "Connecting...";
|
||||
"lng_group_call_leave" = "Leave";
|
||||
@ -2027,6 +2024,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_create_sure" = "Do you really want to start a voice chat in this group?";
|
||||
"lng_group_call_create_sure_channel" = "Are you sure you want to start a voice chat in this channel as your personal account?";
|
||||
"lng_group_call_join_sure_personal" = "Are you sure you want to join this voice chat as your personal account?";
|
||||
"lng_group_call_muted_no_camera" = "You can't turn on video while you're muted by admin.";
|
||||
"lng_group_call_muted_no_screen" = "You can't share your screen while you're muted by admin.";
|
||||
"lng_group_call_chat_no_camera" = "You can't turn on video in this chat.";
|
||||
"lng_group_call_chat_no_screen" = "You can't share your screen in this chat.";
|
||||
"lng_group_call_failed_screen" = "An error occured. Screencast has stopped.";
|
||||
"lng_group_call_tooltip_screen" = "Share screen";
|
||||
"lng_group_call_tooltip_camera" = "Your camera is off. Click here to enable camera.";
|
||||
"lng_group_call_tooltip_microphone" = "You are on mute. Click here to speak.";
|
||||
"lng_group_call_tooltip_camera_off" = "Disable camera";
|
||||
"lng_group_call_tooltip_force_muted" = "Muted by admin. Click if you want to speak.";
|
||||
"lng_group_call_tooltip_raised_hand" = "You asked to speak. We let the speakers know.";
|
||||
"lng_group_call_also_end" = "End voice chat";
|
||||
"lng_group_call_settings_title" = "Settings";
|
||||
"lng_group_call_invite" = "Invite Member";
|
||||
|
@ -457,8 +457,7 @@ GroupCall::~GroupCall() {
|
||||
}
|
||||
|
||||
bool GroupCall::isSharingScreen() const {
|
||||
return _screenOutgoing
|
||||
&& (_screenOutgoing->state() == Webrtc::VideoState::Active);
|
||||
return _isSharingScreen.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> GroupCall::isSharingScreenValue() const {
|
||||
@ -470,8 +469,7 @@ const std::string &GroupCall::screenSharingEndpoint() const {
|
||||
}
|
||||
|
||||
bool GroupCall::isSharingCamera() const {
|
||||
return _cameraOutgoing
|
||||
&& (_cameraOutgoing->state() == Webrtc::VideoState::Active);
|
||||
return _isSharingCamera.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> GroupCall::isSharingCameraValue() const {
|
||||
@ -507,12 +505,9 @@ void GroupCall::toggleVideo(bool active) {
|
||||
return;
|
||||
}
|
||||
ensureOutgoingVideo();
|
||||
const auto state = active
|
||||
_cameraOutgoing->setState(active
|
||||
? Webrtc::VideoState::Active
|
||||
: Webrtc::VideoState::Inactive;
|
||||
if (_cameraOutgoing->state() != state) {
|
||||
_cameraOutgoing->setState(state);
|
||||
}
|
||||
: Webrtc::VideoState::Inactive);
|
||||
}
|
||||
|
||||
void GroupCall::toggleScreenSharing(std::optional<QString> uniqueId) {
|
||||
@ -529,9 +524,7 @@ void GroupCall::toggleScreenSharing(std::optional<QString> uniqueId) {
|
||||
}
|
||||
const auto changed = (_screenDeviceId != *uniqueId);
|
||||
_screenDeviceId = *uniqueId;
|
||||
if (_screenOutgoing->state() != Webrtc::VideoState::Active) {
|
||||
_screenOutgoing->setState(Webrtc::VideoState::Active);
|
||||
}
|
||||
_screenOutgoing->setState(Webrtc::VideoState::Active);
|
||||
if (changed) {
|
||||
_screenCapture->switchToDevice(uniqueId->toStdString());
|
||||
}
|
||||
@ -1110,6 +1103,9 @@ void GroupCall::rejoinPresentation() {
|
||||
LOG(("Call Error: "
|
||||
"Could not screen join, error: %1").arg(type));
|
||||
_screenOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
_errors.fire_copy(mutedByAdmin()
|
||||
? Error::MutedNoScreen
|
||||
: Error::ScreenFailed);
|
||||
}
|
||||
}).send();
|
||||
});
|
||||
@ -1669,6 +1665,48 @@ void GroupCall::setupMediaDevices() {
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
bool GroupCall::emitShareCameraError() {
|
||||
const auto emitError = [=](Error error) {
|
||||
emitShareCameraError(error);
|
||||
return true;
|
||||
};
|
||||
if (const auto real = lookupReal(); real && !real->canStartVideo()) {
|
||||
return emitError(Error::DisabledNoCamera);
|
||||
} else if (mutedByAdmin()) {
|
||||
return emitError(Error::MutedNoCamera);
|
||||
} else if (Webrtc::GetVideoInputList().empty()) {
|
||||
return emitError(Error::NoCamera);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GroupCall::emitShareCameraError(Error error) {
|
||||
if (_cameraOutgoing) {
|
||||
_cameraOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
}
|
||||
_errors.fire_copy(error);
|
||||
}
|
||||
|
||||
bool GroupCall::emitShareScreenError() {
|
||||
const auto emitError = [=](Error error) {
|
||||
emitShareScreenError(error);
|
||||
return true;
|
||||
};
|
||||
if (const auto real = lookupReal(); real && !real->canStartVideo()) {
|
||||
return emitError(Error::DisabledNoScreen);
|
||||
} else if (mutedByAdmin()) {
|
||||
return emitError(Error::MutedNoScreen);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GroupCall::emitShareScreenError(Error error) {
|
||||
if (_screenOutgoing) {
|
||||
_screenOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
}
|
||||
_errors.fire_copy(error);
|
||||
}
|
||||
|
||||
void GroupCall::ensureOutgoingVideo() {
|
||||
Expects(_id != 0);
|
||||
|
||||
@ -1682,33 +1720,21 @@ void GroupCall::ensureOutgoingVideo() {
|
||||
Webrtc::VideoState::Inactive,
|
||||
_requireARGB32);
|
||||
|
||||
using namespace rpl::mappers;
|
||||
_isSharingCamera = _cameraOutgoing->stateValue(
|
||||
) | rpl::map(_1 == Webrtc::VideoState::Active);
|
||||
_isSharingScreen = _screenOutgoing->stateValue(
|
||||
) | rpl::map(_1 == Webrtc::VideoState::Active);
|
||||
|
||||
//static const auto hasDevices = [] {
|
||||
// return !Webrtc::GetVideoInputList().empty();
|
||||
//};
|
||||
_cameraOutgoing->stateValue(
|
||||
) | rpl::start_with_next([=](Webrtc::VideoState state) {
|
||||
//if (state != Webrtc::VideoState::Inactive && !hasDevices()) {
|
||||
//_errors.fire({ ErrorType::NoCamera }); // #TODO calls
|
||||
//_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
//} else if (state != Webrtc::VideoState::Inactive
|
||||
// && _instance
|
||||
// && !_instance->supportsVideo()) {
|
||||
// _errors.fire({ ErrorType::NotVideoCall });
|
||||
// _videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
/*} else */if (state != Webrtc::VideoState::Inactive) {
|
||||
const auto active = (state != Webrtc::VideoState::Inactive);
|
||||
if (active) {
|
||||
// Paused not supported right now.
|
||||
Assert(state == Webrtc::VideoState::Active);
|
||||
if (!_cameraCapture) {
|
||||
if (emitShareCameraError()) {
|
||||
return;
|
||||
} else if (!_cameraCapture) {
|
||||
_cameraCapture = _delegate->groupCallGetVideoCapture(
|
||||
_cameraInputId);
|
||||
if (!_cameraCapture) {
|
||||
return emitShareCameraError(Error::NoCamera);
|
||||
_cameraOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
_errors.fire_copy(Error::NoCamera);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -1721,31 +1747,33 @@ void GroupCall::ensureOutgoingVideo() {
|
||||
} else if (_cameraCapture) {
|
||||
_cameraCapture->setState(tgcalls::VideoState::Inactive);
|
||||
}
|
||||
markEndpointActive({ _joinAs, _cameraEndpoint }, isSharingCamera());
|
||||
_isSharingCamera = active;
|
||||
markEndpointActive({ _joinAs, _cameraEndpoint }, active);
|
||||
sendSelfUpdate(SendUpdateType::VideoMuted);
|
||||
applyMeInCallLocally();
|
||||
}, _lifetime);
|
||||
|
||||
_screenOutgoing->stateValue(
|
||||
) | rpl::start_with_next([=](Webrtc::VideoState state) {
|
||||
if (state != Webrtc::VideoState::Inactive) {
|
||||
const auto active = (state != Webrtc::VideoState::Inactive);
|
||||
if (active) {
|
||||
// Paused not supported right now.
|
||||
Assert(state == Webrtc::VideoState::Active);
|
||||
if (!_screenCapture) {
|
||||
_screenCapture = std::shared_ptr<tgcalls::VideoCaptureInterface>(
|
||||
tgcalls::VideoCaptureInterface::Create(
|
||||
tgcalls::StaticThreads::getThreads(),
|
||||
_screenDeviceId.toStdString()));
|
||||
if (emitShareScreenError()) {
|
||||
return;
|
||||
} else if (!_screenCapture) {
|
||||
_screenCapture = std::shared_ptr<
|
||||
tgcalls::VideoCaptureInterface
|
||||
>(tgcalls::VideoCaptureInterface::Create(
|
||||
tgcalls::StaticThreads::getThreads(),
|
||||
_screenDeviceId.toStdString()));
|
||||
if (!_screenCapture) {
|
||||
_screenOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
return;
|
||||
return emitShareScreenError(Error::ScreenFailed);
|
||||
}
|
||||
const auto weak = base::make_weak(this);
|
||||
_screenCapture->setOnFatalError([=] {
|
||||
crl::on_main(weak, [=] {
|
||||
_screenOutgoing->setState(
|
||||
Webrtc::VideoState::Inactive);
|
||||
// #TODO calls show error toast, receive here device.
|
||||
emitShareScreenError(Error::ScreenFailed);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
@ -1758,7 +1786,8 @@ void GroupCall::ensureOutgoingVideo() {
|
||||
} else if (_screenCapture) {
|
||||
_screenCapture->setState(tgcalls::VideoState::Inactive);
|
||||
}
|
||||
markEndpointActive({ _joinAs, _screenEndpoint }, isSharingScreen());
|
||||
_isSharingScreen = active;
|
||||
markEndpointActive({ _joinAs, _screenEndpoint }, active);
|
||||
_screenJoinState.nextActionPending = true;
|
||||
checkNextJoinAction();
|
||||
}, _lifetime);
|
||||
@ -2457,8 +2486,7 @@ void GroupCall::sendSelfUpdate(SendUpdateType type) {
|
||||
MTP_bool(muted() != MuteState::Active),
|
||||
MTP_int(100000), // volume
|
||||
MTP_bool(muted() == MuteState::RaisedHand),
|
||||
MTP_bool(!_cameraOutgoing
|
||||
|| _cameraOutgoing->state() != Webrtc::VideoState::Active)
|
||||
MTP_bool(!isSharingCamera())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_updateMuteRequestId = 0;
|
||||
_peer->session().api().applyUpdates(result);
|
||||
|
@ -48,6 +48,7 @@ struct ParticipantState;
|
||||
struct JoinInfo;
|
||||
struct RejoinEvent;
|
||||
enum class VideoQuality;
|
||||
enum class Error;
|
||||
} // namespace Group
|
||||
|
||||
enum class MuteState {
|
||||
@ -225,6 +226,13 @@ public:
|
||||
void startScheduledNow();
|
||||
void toggleScheduleStartSubscribed(bool subscribed);
|
||||
|
||||
bool emitShareScreenError();
|
||||
bool emitShareCameraError();
|
||||
|
||||
[[nodiscard]] rpl::producer<Group::Error> errors() const {
|
||||
return _errors.events();
|
||||
}
|
||||
|
||||
void addVideoOutput(
|
||||
const std::string &endpoint,
|
||||
not_null<Webrtc::VideoTrack*> track);
|
||||
@ -367,6 +375,7 @@ public:
|
||||
|
||||
private:
|
||||
using GlobalShortcutValue = base::GlobalShortcutValue;
|
||||
using Error = Group::Error;
|
||||
struct SinkPointer;
|
||||
|
||||
static constexpr uint32 kDisabledSsrc = uint32(-1);
|
||||
@ -421,6 +430,9 @@ private:
|
||||
bool tryCreateScreencast();
|
||||
void destroyScreencast();
|
||||
|
||||
void emitShareCameraError(Error error);
|
||||
void emitShareScreenError(Error error);
|
||||
|
||||
void setState(State state);
|
||||
void finish(FinishType type);
|
||||
void maybeSendMutedUpdate(MuteState previous);
|
||||
@ -490,6 +502,7 @@ private:
|
||||
rpl::event_stream<not_null<Data::GroupCall*>> _realChanges;
|
||||
rpl::variable<State> _state = State::Creating;
|
||||
base::flat_set<uint32> _unresolvedSsrcs;
|
||||
rpl::event_stream<Error> _errors;
|
||||
bool _recordingStoppedByMe = false;
|
||||
bool _requestedVideoChannelsUpdateScheduled = false;
|
||||
|
||||
|
@ -59,4 +59,13 @@ enum class VideoQuality {
|
||||
Full,
|
||||
};
|
||||
|
||||
enum class Error {
|
||||
NoCamera,
|
||||
ScreenFailed,
|
||||
MutedNoCamera,
|
||||
MutedNoScreen,
|
||||
DisabledNoCamera,
|
||||
DisabledNoScreen,
|
||||
};
|
||||
|
||||
} // namespace Calls::Group
|
||||
|
@ -66,6 +66,7 @@ constexpr auto kRecordingOpacity = 0.6;
|
||||
constexpr auto kStartNoConfirmation = TimeId(10);
|
||||
constexpr auto kControlsBackgroundOpacity = 0.8;
|
||||
constexpr auto kOverrideActiveColorBgAlpha = 172;
|
||||
constexpr auto kErrorDuration = 2 * crl::time(1000);
|
||||
|
||||
class InviteController final : public ParticipantsBoxController {
|
||||
public:
|
||||
@ -437,9 +438,7 @@ Panel::Panel(not_null<GroupCall*> call)
|
||||
initControls();
|
||||
initLayout();
|
||||
showAndActivate();
|
||||
setupJoinAsChangedToasts();
|
||||
setupTitleChangedToasts();
|
||||
setupAllowedToSpeakToasts();
|
||||
setupToasts();
|
||||
}
|
||||
|
||||
Panel::~Panel() {
|
||||
@ -866,18 +865,12 @@ void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
|
||||
: state == GroupCall::InstanceState::Disconnected
|
||||
? tr::lng_group_call_connecting(tr::now)
|
||||
: mute == MuteState::ForceMuted
|
||||
? (wide
|
||||
? tr::lng_group_call_force_muted_small(tr::now)
|
||||
: tr::lng_group_call_force_muted(tr::now))
|
||||
? tr::lng_group_call_force_muted(tr::now)
|
||||
: mute == MuteState::RaisedHand
|
||||
? (wide
|
||||
? tr::lng_group_call_raised_hand_small(tr::now)
|
||||
: tr::lng_group_call_raised_hand(tr::now))
|
||||
? tr::lng_group_call_raised_hand(tr::now)
|
||||
: mute == MuteState::Muted
|
||||
? tr::lng_group_call_unmute(tr::now)
|
||||
: (wide
|
||||
? tr::lng_group_call_you_are_live_small(tr::now)
|
||||
: tr::lng_group_call_you_are_live(tr::now))),
|
||||
: tr::lng_group_call_you_are_live(tr::now)),
|
||||
.subtext = ((scheduleDate || wide)
|
||||
? QString()
|
||||
: state == GroupCall::InstanceState::Disconnected
|
||||
@ -1206,6 +1199,14 @@ void Panel::toggleWideControls(bool shown) {
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::setupToasts() {
|
||||
setupJoinAsChangedToasts();
|
||||
setupTitleChangedToasts();
|
||||
setupRequestedToSpeakToasts();
|
||||
setupAllowedToSpeakToasts();
|
||||
setupErrorToasts();
|
||||
}
|
||||
|
||||
void Panel::setupJoinAsChangedToasts() {
|
||||
_call->rejoinEvents(
|
||||
) | rpl::filter([](RejoinEvent event) {
|
||||
@ -1270,6 +1271,46 @@ void Panel::setupAllowedToSpeakToasts() {
|
||||
}, widget()->lifetime());
|
||||
}
|
||||
|
||||
void Panel::setupRequestedToSpeakToasts() {
|
||||
_call->mutedValue(
|
||||
) | rpl::combine_previous(
|
||||
) | rpl::start_with_next([=](MuteState was, MuteState now) {
|
||||
if (was == MuteState::ForceMuted && now == MuteState::RaisedHand) {
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = widget(),
|
||||
.text = tr::lng_group_call_tooltip_raised_hand(tr::now),
|
||||
});
|
||||
}
|
||||
}, widget()->lifetime());
|
||||
}
|
||||
|
||||
void Panel::setupErrorToasts() {
|
||||
_call->errors(
|
||||
) | rpl::start_with_next([=](Error error) {
|
||||
const auto key = [&] {
|
||||
switch (error) {
|
||||
case Error::NoCamera: return tr::lng_call_error_no_camera;
|
||||
case Error::ScreenFailed:
|
||||
return tr::lng_group_call_failed_screen;
|
||||
case Error::MutedNoCamera:
|
||||
return tr::lng_group_call_muted_no_camera;
|
||||
case Error::MutedNoScreen:
|
||||
return tr::lng_group_call_muted_no_screen;
|
||||
case Error::DisabledNoCamera:
|
||||
return tr::lng_group_call_chat_no_camera;
|
||||
case Error::DisabledNoScreen:
|
||||
return tr::lng_group_call_chat_no_screen;
|
||||
}
|
||||
Unexpected("Error in Calls::Group::Panel::setupErrorToasts.");
|
||||
}();
|
||||
Ui::ShowMultilineToast({
|
||||
.parentOverride = widget(),
|
||||
.text = { key(tr::now) },
|
||||
.duration = kErrorDuration,
|
||||
});
|
||||
}, widget()->lifetime());
|
||||
}
|
||||
|
||||
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||
const auto validateRecordingMark = [=](bool recording) {
|
||||
if (!recording && _recordingMark) {
|
||||
@ -1397,7 +1438,7 @@ void Panel::refreshTopButton() {
|
||||
}
|
||||
|
||||
void Panel::chooseShareScreenSource() {
|
||||
if (!_call->mutedByAdmin()) {
|
||||
if (!_call->emitShareScreenError()) {
|
||||
Ui::DesktopCapture::ChooseSource(this);
|
||||
}
|
||||
}
|
||||
|
@ -87,9 +87,12 @@ private:
|
||||
void setupScheduledLabels(rpl::producer<TimeId> date);
|
||||
void setupMembers();
|
||||
void setupVideo();
|
||||
void setupToasts();
|
||||
void setupJoinAsChangedToasts();
|
||||
void setupTitleChangedToasts();
|
||||
void setupRequestedToSpeakToasts();
|
||||
void setupAllowedToSpeakToasts();
|
||||
void setupErrorToasts();
|
||||
void setupRealMuteButtonState(not_null<Data::GroupCall*> real);
|
||||
|
||||
bool handleClose();
|
||||
|
Loading…
Reference in New Issue
Block a user