Improving user experience on first time visit

canary
WolverinDEV 2020-09-02 14:34:42 +02:00
parent 1a13e11063
commit 398114434a
5 changed files with 111 additions and 67 deletions

View File

@ -4,6 +4,7 @@
- Improved default recorder device detection (selects by default the best device) - Improved default recorder device detection (selects by default the best device)
- Decreased playback latency for the web client - Decreased playback latency for the web client
- The newcomer help texts are now forward able via the next button - The newcomer help texts are now forward able via the next button
- Do not show the what's new screen for new users
* **31.08.20** * **31.08.20**
- Reworked the audio decode system - Reworked the audio decode system

View File

View File

@ -462,23 +462,6 @@ export namespace modal {
} }
} }
export interface newcomer {
"show_step": {
"step": "welcome" | "microphone" | "identity" | "finish"
},
"exit_guide": {
ask_yesno: boolean
},
"modal-shown": {},
"step-status": {
next_button: boolean,
previous_button: boolean
}
}
export namespace settings { export namespace settings {
export type ProfileInfo = { export type ProfileInfo = {
id: string, id: string,

View File

@ -1,7 +1,6 @@
import {createModal, Modal} from "tc-shared/ui/elements/Modal"; import {createModal, Modal} from "tc-shared/ui/elements/Modal";
import {tra} from "tc-shared/i18n/localize"; import {tra} from "tc-shared/i18n/localize";
import {Registry} from "tc-shared/events"; import {Registry} from "tc-shared/events";
import { modal as emodal } from "tc-shared/events";
import {modal_settings} from "tc-shared/ui/modal/ModalSettings"; import {modal_settings} from "tc-shared/ui/modal/ModalSettings";
import {profiles} from "tc-shared/profiles/ConnectionProfile"; import {profiles} from "tc-shared/profiles/ConnectionProfile";
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo"; import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
@ -9,6 +8,23 @@ import {initialize_audio_microphone_controller, MicrophoneSettingsEvents} from "
import {MicrophoneSettings} from "tc-shared/ui/modal/settings/MicrophoneRenderer"; import {MicrophoneSettings} from "tc-shared/ui/modal/settings/MicrophoneRenderer";
import * as React from "react"; import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import { modal as emodal } from "tc-shared/events";
export interface EventModalNewcomer {
"show_step": {
"step": "welcome" | "microphone" | "identity" | "finish"
},
"exit_guide": {
ask_yesno: boolean
},
"modal-shown": {},
"action-next-help": {},
"step-status": {
allowNextStep: boolean,
allowPreviousStep: boolean
}
}
const next_step: {[key: string]:string} = { const next_step: {[key: string]:string} = {
"welcome": "microphone", "welcome": "microphone",
@ -37,7 +53,7 @@ export function openModalNewcomer() : Modal {
closeable: false closeable: false
}); });
const event_registry = new Registry<emodal.newcomer>(); const event_registry = new Registry<EventModalNewcomer>();
event_registry.enableDebug("newcomer"); event_registry.enableDebug("newcomer");
modal.htmlTag.find(".modal-body").addClass("modal-newcomer"); modal.htmlTag.find(".modal-body").addClass("modal-newcomer");
@ -49,13 +65,14 @@ export function openModalNewcomer() : Modal {
initializeStepFinish(modal.htmlTag.find(".container-body .step.step-finish"), event_registry); initializeStepFinish(modal.htmlTag.find(".container-body .step.step-finish"), event_registry);
event_registry.on("exit_guide", event => { event_registry.on("exit_guide", event => {
if(event.ask_yesno) if(event.ask_yesno) {
spawnYesNo(tr("Are you sure?"), tr("Do you really want to skip the basic setup guide?"), result => { spawnYesNo(tr("Are you sure?"), tr("Do you really want to skip the basic setup guide?"), result => {
if(result) if(result)
event_registry.fire("exit_guide", {ask_yesno: false}); event_registry.fire("exit_guide", {ask_yesno: false});
}); });
else } else {
modal.close(); modal.close();
}
}); });
event_registry.fire("show_step", {step: "welcome"}); event_registry.fire("show_step", {step: "welcome"});
@ -64,7 +81,7 @@ export function openModalNewcomer() : Modal {
return modal; return modal;
} }
function initializeBasicFunctionality(tag: JQuery, event_registry: Registry<emodal.newcomer>) { function initializeBasicFunctionality(tag: JQuery, event_registry: Registry<EventModalNewcomer>) {
const container_header = tag.find(".container-header"); const container_header = tag.find(".container-header");
const tag_body = tag.find(".container-body .body"); const tag_body = tag.find(".container-body .body");
@ -82,6 +99,7 @@ function initializeBasicFunctionality(tag: JQuery, event_registry: Registry<emod
const buttons = tag.find(".buttons"); const buttons = tag.find(".buttons");
const button_last_step = buttons.find(".button-last-step"); const button_last_step = buttons.find(".button-last-step");
const button_next_step = buttons.find(".button-next-step"); const button_next_step = buttons.find(".button-next-step");
let allowNextStep = true;
button_last_step.on('click', () => { button_last_step.on('click', () => {
if(last_step[current_step]) if(last_step[current_step])
@ -92,10 +110,16 @@ function initializeBasicFunctionality(tag: JQuery, event_registry: Registry<emod
let current_step; let current_step;
button_next_step.on('click', event => { button_next_step.on('click', event => {
if(next_step[current_step]) if(!allowNextStep) {
event_registry.fire("action-next-help");
return;
}
if(next_step[current_step]) {
event_registry.fire("show_step", { step: next_step[current_step] as any }); event_registry.fire("show_step", { step: next_step[current_step] as any });
else } else {
event_registry.fire("exit_guide", {ask_yesno: false}); event_registry.fire("exit_guide", { ask_yesno: false });
}
}); });
event_registry.on("show_step", event => { event_registry.on("show_step", event => {
@ -104,46 +128,45 @@ function initializeBasicFunctionality(tag: JQuery, event_registry: Registry<emod
button_last_step.text(last_step[current_step] ? tr("Last step") : tr("Skip guide")); button_last_step.text(last_step[current_step] ? tr("Last step") : tr("Skip guide"));
}); });
event_registry.on("show_step", () => button_next_step.prop("disabled", true)); event_registry.on("show_step", () => button_next_step.prop("disabled", false));
event_registry.on("show_step", () => button_last_step.prop("disabled", true)); event_registry.on("show_step", () => button_last_step.prop("disabled", true));
event_registry.on("step-status", event => button_next_step.prop("disabled", !event.next_button)); event_registry.on("step-status", event => allowNextStep = event.allowNextStep);
event_registry.on("step-status", event => button_last_step.prop("disabled", !event.previous_button)); event_registry.on("step-status", event => button_last_step.prop("disabled", !event.allowPreviousStep));
} }
} }
function initializeStepWelcome(tag: JQuery, event_registry: Registry<emodal.newcomer>) { function initializeStepWelcome(tag: JQuery, event_registry: Registry<EventModalNewcomer>) {
event_registry.on("show_step", e => { event_registry.on("show_step", e => {
if(e.step !== "welcome") return; if(e.step !== "welcome") return;
event_registry.fire_async("step-status", { next_button: true, previous_button: true }); event_registry.fire_async("step-status", { allowNextStep: true, allowPreviousStep: true });
}); });
} }
function initializeStepFinish(tag: JQuery, event_registry: Registry<emodal.newcomer>) { function initializeStepFinish(tag: JQuery, event_registry: Registry<EventModalNewcomer>) {
event_registry.on("show_step", e => { event_registry.on("show_step", e => {
if(e.step !== "finish") return; if(e.step !== "finish") return;
event_registry.fire_async("step-status", {next_button: true, previous_button: true }); event_registry.fire_async("step-status", {allowNextStep: true, allowPreviousStep: true });
}); });
} }
function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.newcomer>) { function initializeStepIdentity(tag: JQuery, event_registry: Registry<EventModalNewcomer>) {
const profile_events = new Registry<emodal.settings.profiles>(); const profile_events = new Registry<emodal.settings.profiles>();
profile_events.enableDebug("settings-identity"); profile_events.enableDebug("settings-identity");
modal_settings.initialize_identity_profiles_controller(profile_events); modal_settings.initialize_identity_profiles_controller(profile_events);
modal_settings.initialize_identity_profiles_view(tag, profile_events, { forum_setuppable: false }); modal_settings.initialize_identity_profiles_view(tag, profile_events, { forum_setuppable: false });
let step_shown = false; let stepShown = false;
let help_animation_done = false; let help_animation_done = false;
const profiles_valid = () => profiles().findIndex(e => e.valid()) !== -1;
const update_step_status = () => { const update_step_status = () => {
event_registry.fire_async("step-status", { next_button: help_animation_done && profiles_valid(), previous_button: help_animation_done }); event_registry.fire_async("step-status", { allowNextStep: help_animation_done, allowPreviousStep: help_animation_done });
}; };
profile_events.on("query-profile-validity-result", event => step_shown && event.status === "success" && event.valid && update_step_status()); profile_events.on("query-profile-validity-result", event => stepShown && event.status === "success" && event.valid && update_step_status());
event_registry.on("show_step", e => { event_registry.on("show_step", e => {
step_shown = e.step === "identity"; stepShown = e.step === "identity";
if(!step_shown) return; if(!stepShown) return;
update_step_status(); update_step_status();
}); });
@ -157,15 +180,24 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
const container_profile_settings = tag.find(".highlight-profile-settings"); const container_profile_settings = tag.find(".highlight-profile-settings");
const container_identity_settings = tag.find(".highlight-identity-settings"); const container_identity_settings = tag.find(".highlight-identity-settings");
let is_first_show = true; let helpStep = 0;
const set_help_text = text => {
container_help_text.empty();
text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e)));
};
event_registry.on("show_step", event => { event_registry.on("show_step", event => {
if(!is_first_show || event.step !== "identity") return; if(helpStep > 0 || event.step !== "identity") {
is_first_show = false; document.body.removeEventListener("mousedown", listenerClick);
return;
container.addClass("help-shown"); }
document.body.addEventListener("mousedown", listenerClick);
steps[helpStep++]();
});
const show_initial_help = () => {
const text = tr( /* @tr-ignore */ const text = tr( /* @tr-ignore */
"After you've successfully set upped your microphone,\n" + "After you've successfully set upped your microphone,\n" +
"lets setup some profiles and identities!\n" + "lets setup some profiles and identities!\n" +
@ -177,12 +209,7 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
"To continue click anywhere on the screen." "To continue click anywhere on the screen."
); );
set_help_text(text); set_help_text(text);
$("body").one('mousedown', event => show_profile_list_help()); container.addClass("help-shown");
});
const set_help_text = text => {
container_help_text.empty();
text.split("\n").forEach(e => container_help_text.append(e == "" ? $.spawn("br") : $.spawn("a").text(e)));
}; };
const show_profile_list_help = () => { const show_profile_list_help = () => {
@ -219,7 +246,6 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
"To continue click anywhere on the screen." "To continue click anywhere on the screen."
); );
set_help_text(text); set_help_text(text);
$("body").one('mousedown', event => show_profile_settings_help());
}; };
const show_profile_settings_help = () => { const show_profile_settings_help = () => {
@ -251,8 +277,6 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
)); ));
update_position(); update_position();
container_help_text.off('resize').on('resize', update_position); container_help_text.off('resize').on('resize', update_position);
$("body").one('mousedown', event => show_identity_settings_help());
}; };
const show_identity_settings_help = () => { const show_identity_settings_help = () => {
@ -279,8 +303,6 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
)); ));
update_position(); update_position();
container_help_text.off('resize').on('resize', update_position); container_help_text.off('resize').on('resize', update_position);
$("body").one('mousedown', event => hide_help());
}; };
const hide_help = () => { const hide_help = () => {
@ -290,22 +312,49 @@ function initializeStepIdentity(tag: JQuery, event_registry: Registry<emodal.new
container_help_text.off('resize'); container_help_text.off('resize');
help_animation_done = true; help_animation_done = true;
update_step_status();
}; };
const steps = [
show_initial_help,
show_profile_list_help,
show_profile_settings_help,
show_identity_settings_help,
hide_help
];
const listenerClick = () => event_registry.fire("action-next-help");
event_registry.on("action-next-help", () => {
if(!stepShown) {
return;
}
const fn = steps[helpStep++];
if(typeof fn === "function") {
fn();
}
update_step_status();
document.body.addEventListener("mousedown", listenerClick);
});
} }
} }
function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.newcomer>, modal: Modal) { function initializeStepMicrophone(tag: JQuery, event_registry: Registry<EventModalNewcomer>, modal: Modal) {
let helpStep = 0; let helpStep = 0;
let stepShown = false;
const settingEvents = new Registry<MicrophoneSettingsEvents>(); const settingEvents = new Registry<MicrophoneSettingsEvents>();
settingEvents.on("query_help", () => settingEvents.fire_async("notify_highlight", { field: helpStep <= 2 ? ("hs-" + helpStep) as any : undefined })); settingEvents.on("query_help", () => settingEvents.fire_async("notify_highlight", { field: helpStep <= 2 ? ("hs-" + helpStep) as any : undefined }));
settingEvents.on("action_help_click", () => { settingEvents.on("action_help_click", () => {
if(!stepShown) {
return;
}
helpStep++; helpStep++;
settingEvents.fire("query_help"); settingEvents.fire("query_help");
event_registry.fire_async("step-status", { next_button: helpStep > 2, previous_button: helpStep > 2 }) event_registry.fire_async("step-status", { allowNextStep: helpStep > 2, allowPreviousStep: helpStep > 2 })
}); });
event_registry.on("action-next-help", () => settingEvents.fire("action_help_click"));
initialize_audio_microphone_controller(settingEvents); initialize_audio_microphone_controller(settingEvents);
ReactDOM.render(<MicrophoneSettings events={settingEvents} />, tag[0]); ReactDOM.render(<MicrophoneSettings events={settingEvents} />, tag[0]);
@ -317,7 +366,11 @@ function initializeStepMicrophone(tag: JQuery, event_registry: Registry<emodal.n
event_registry.on("show_step", event => { event_registry.on("show_step", event => {
if(event.step !== "microphone") return; stepShown = event.step === "microphone";
event_registry.fire_async("step-status", { next_button: helpStep > 2, previous_button: helpStep > 2 }); if(!stepShown) {
return;
}
event_registry.fire_async("step-status", { allowNextStep: helpStep > 2, allowPreviousStep: helpStep > 2 });
}); });
} }

View File

@ -2,6 +2,7 @@ import {Updater} from "./Updater";
import {ChangeLog} from "tc-shared/update/ChangeLog"; import {ChangeLog} from "tc-shared/update/ChangeLog";
import {spawnUpdatedModal} from "tc-shared/ui/modal/whats-new/Controller"; import {spawnUpdatedModal} from "tc-shared/ui/modal/whats-new/Controller";
const kIsNewUserKey = "updater-set";
let updaterUi: Updater; let updaterUi: Updater;
let updaterNative: Updater; let updaterNative: Updater;
@ -28,15 +29,21 @@ function getChangedChangeLog(updater: Updater) : ChangeLog | undefined {
} }
export function checkForUpdatedApp() { export function checkForUpdatedApp() {
let changesUI = updaterUi ? getChangedChangeLog(updaterUi) : undefined; if(localStorage.getItem(kIsNewUserKey)) {
let changesNative = updaterNative ? getChangedChangeLog(updaterNative) : undefined; let changesUI = updaterUi ? getChangedChangeLog(updaterUi) : undefined;
let changesNative = updaterNative ? getChangedChangeLog(updaterNative) : undefined;
if(changesUI !== undefined || changesNative !== undefined) { if(changesUI !== undefined || changesNative !== undefined) {
spawnUpdatedModal({ spawnUpdatedModal({
changesUI: changesUI, changesUI: changesUI,
changesClient: changesNative changesClient: changesNative
}); });
updaterUi?.updateUsedVersion();
updaterNative?.updateUsedVersion();
}
} else {
localStorage.setItem(kIsNewUserKey, "1");
updaterUi?.updateUsedVersion(); updaterUi?.updateUsedVersion();
updaterNative?.updateUsedVersion(); updaterNative?.updateUsedVersion();
} }