mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
user oboarding flow (#54)
* init * telemetry toggle * finish styling and functionality * remove unused style * rever some styles * use goroutine in sending telementry updates * remove wave-modal class from AlertModal * icons and button state fixes * minor change with goroutines * use default cursor not not-allowed
This commit is contained in:
parent
616a7d2dc0
commit
e7b58c1077
5
src/app/assets/icons/github.svg
Normal file
5
src/app/assets/icons/github.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="github-fill">
|
||||
<path id="Vector" d="M24 4C12.95 4 4 12.95 4 24C3.99773 28.1986 5.31763 32.2912 7.77243 35.6974C10.2272 39.1035 13.6923 41.6501 17.676 42.976C18.676 43.15 19.05 42.55 19.05 42.024C19.05 41.55 19.024 39.976 19.024 38.3C14 39.226 12.7 37.076 12.3 35.95C12.074 35.374 11.1 33.6 10.25 33.124C9.55 32.75 8.55 31.824 10.224 31.8C11.8 31.774 12.924 33.25 13.3 33.85C15.1 36.874 17.976 36.024 19.124 35.5C19.3 34.2 19.824 33.326 20.4 32.826C15.95 32.326 11.3 30.6 11.3 22.95C11.3 20.774 12.074 18.976 13.35 17.574C13.15 17.074 12.45 15.024 13.55 12.274C13.55 12.274 15.224 11.75 19.05 14.326C20.6781 13.8741 22.3603 13.6467 24.05 13.65C25.75 13.65 27.45 13.874 29.05 14.324C32.874 11.724 34.55 12.276 34.55 12.276C35.65 15.026 34.95 17.076 34.75 17.576C36.024 18.976 36.8 20.75 36.8 22.95C36.8 30.626 32.126 32.326 27.676 32.826C28.4 33.45 29.026 34.65 29.026 36.526C29.026 39.2 29 41.35 29 42.026C29 42.55 29.376 43.174 30.376 42.974C34.3461 41.6336 37.7958 39.082 40.2398 35.6783C42.6838 32.2746 43.9989 28.1902 44 24C44 12.95 35.05 4 24 4Z" fill="#58C142"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
5
src/app/assets/icons/help_filled.svg
Normal file
5
src/app/assets/icons/help_filled.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="help">
|
||||
<path id="Vector" d="M23.9468 3.16714C12.682 2.96414 3.38555 11.9315 3.18255 23.1963C2.97955 34.461 11.9469 43.7575 23.2117 43.9605C34.4764 44.1635 43.7729 35.1961 43.9759 23.9314C44.1789 12.6666 35.2115 3.37014 23.9468 3.16714ZM23.7413 14.5653C22.3462 14.5401 21.1521 15.4731 20.7957 16.7612C20.5306 17.7193 19.539 18.2811 18.5809 18.0161C17.6228 17.751 17.061 16.7594 17.3261 15.8012C18.1098 12.9686 20.7289 10.9104 23.8062 10.9659C27.4508 11.0316 30.3519 14.0392 30.2863 17.6837C30.2339 20.59 28.6376 22.0717 27.4405 23.1492L27.4205 23.1672C26.2542 24.2169 25.5369 24.8629 25.3131 26.2501C25.1548 27.2315 24.2308 27.8987 23.2494 27.7406C22.2679 27.5822 21.6007 26.6583 21.759 25.6767C22.1877 23.0184 23.7599 21.6119 24.867 20.6214L25.032 20.4736C26.1224 19.492 26.6636 18.9119 26.6869 17.6189C26.7167 15.9623 25.3978 14.5951 23.7413 14.5653ZM25.8059 33.2055C25.782 34.5308 24.6883 35.5858 23.363 35.5619C22.0378 35.538 20.9828 34.4443 21.0067 33.119C21.0305 31.7937 22.1243 30.7388 23.4495 30.7627C24.7748 30.7865 25.8298 31.8802 25.8059 33.2055Z" fill="#58C142"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
367
src/app/assets/icons/line/modals.tsx
Normal file
367
src/app/assets/icons/line/modals.tsx
Normal file
@ -0,0 +1,367 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import * as mobx from "mobx";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { If, For } from "tsx-control-statements/components";
|
||||
import cn from "classnames";
|
||||
import dayjs from "dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel, GlobalCommandRunner } from "../../../model/model";
|
||||
import { Markdown } from "../common";
|
||||
import * as util from "../../../util/util";
|
||||
|
||||
import { ReactComponent as XmarkIcon } from "../../assets/icons/line/xmark.svg";
|
||||
import { ReactComponent as WarningIcon } from "../../assets/icons/line/triangle-exclamation.svg";
|
||||
import { ReactComponent as ShieldCheck } from "../../assets/icons/line/shield_check.svg";
|
||||
import { ReactComponent as Help } from "../../assets/icons/line/help_filled.svg";
|
||||
import { ReactComponent as Github } from "../../assets/icons/line/github.svg";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
type OV<V> = mobx.IObservableValue<V>;
|
||||
|
||||
@mobxReact.observer
|
||||
class DisconnectedModal extends React.Component<{}, {}> {
|
||||
logRef: any = React.createRef();
|
||||
showLog: mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
||||
|
||||
@boundMethod
|
||||
restartServer() {
|
||||
GlobalModel.restartWaveSrv();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
tryReconnect() {
|
||||
GlobalModel.ws.connectNow("manual");
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.logRef.current != null) {
|
||||
this.logRef.current.scrollTop = this.logRef.current.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.logRef.current != null) {
|
||||
this.logRef.current.scrollTop = this.logRef.current.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleShowLog(): void {
|
||||
mobx.action(() => {
|
||||
this.showLog.set(!this.showLog.get());
|
||||
})();
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let logLine: string = null;
|
||||
let idx: number = 0;
|
||||
return (
|
||||
<div className="prompt-modal disconnected-modal modal is-active">
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">Prompt Client Disconnected</div>
|
||||
</div>
|
||||
<If condition={this.showLog.get()}>
|
||||
<div className="inner-content">
|
||||
<div className="ws-log" ref={this.logRef}>
|
||||
<For each="logLine" index="idx" of={GlobalModel.ws.wsLog}>
|
||||
<div key={idx} className="ws-logline">
|
||||
{logLine}
|
||||
</div>
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
<footer>
|
||||
<div className="footer-text-link" style={{ marginLeft: 10 }} onClick={this.handleShowLog}>
|
||||
<If condition={!this.showLog.get()}>
|
||||
<i className="fa-sharp fa-solid fa-plus" /> Show Log
|
||||
</If>
|
||||
<If condition={this.showLog.get()}>
|
||||
<i className="fa-sharp fa-solid fa-minus" /> Hide Log
|
||||
</If>
|
||||
</div>
|
||||
<div className="flex-spacer" />
|
||||
<button onClick={this.tryReconnect} className="button">
|
||||
<span className="icon">
|
||||
<i className="fa-sharp fa-solid fa-rotate" />
|
||||
</span>
|
||||
<span>Try Reconnect</span>
|
||||
</button>
|
||||
<button onClick={this.restartServer} className="button is-danger" style={{ marginLeft: 10 }}>
|
||||
<WarningIcon className="icon" />
|
||||
<span>Restart Server</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class ClientStopModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
refreshClient() {
|
||||
GlobalModel.refreshClient();
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = GlobalModel;
|
||||
let cdata = model.clientData.get();
|
||||
let title = "Client Not Ready";
|
||||
return (
|
||||
<div className="prompt-modal client-stop-modal modal is-active">
|
||||
<div className="modal-background"></div>
|
||||
<div className="modal-content">
|
||||
<div className="message-header">
|
||||
<div className="modal-title">[prompt] {title}</div>
|
||||
</div>
|
||||
<div className="inner-content">
|
||||
<If condition={cdata == null}>
|
||||
<div>Cannot get client data.</div>
|
||||
</If>
|
||||
</div>
|
||||
<footer>
|
||||
<button onClick={this.refreshClient} className="button">
|
||||
<span className="icon">
|
||||
<i className="fa-sharp fa-solid fa-rotate" />
|
||||
</span>
|
||||
<span>Hard Refresh Client</span>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class LoadingSpinner extends React.Component<{}, {}> {
|
||||
render() {
|
||||
return (
|
||||
<div className="loading-spinner">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class AlertModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
GlobalModel.cancelAlert();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleOK(): void {
|
||||
GlobalModel.confirmAlert();
|
||||
}
|
||||
|
||||
render() {
|
||||
let message = GlobalModel.alertMessage.get();
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
let title = message.title ?? (message.confirm ? "Confirm" : "Alert");
|
||||
let isConfirm = message.confirm;
|
||||
return (
|
||||
<div className="modal prompt-modal wave-modal is-active alert-modal">
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<header>
|
||||
<p className="modal-title">
|
||||
<WarningIcon className="icon" />
|
||||
{title}
|
||||
</p>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<If condition={message.markdown}>
|
||||
<Markdown text={message.message} extraClassName="inner-content" />
|
||||
</If>
|
||||
<If condition={!message.markdown}>
|
||||
<div className="inner-content content">
|
||||
<p>{message.message}</p>
|
||||
</div>
|
||||
</If>
|
||||
<footer>
|
||||
<If condition={isConfirm}>
|
||||
<div onClick={this.closeModal} className="button is-prompt-cancel is-outlined is-small">
|
||||
Cancel
|
||||
</div>
|
||||
<div onClick={this.handleOK} className="button is-prompt-green is-outlined is-small">
|
||||
OK
|
||||
</div>
|
||||
</If>
|
||||
<If condition={!isConfirm}>
|
||||
<div onClick={this.handleOK} className="button is-prompt-green is-small">
|
||||
OK
|
||||
</div>
|
||||
</If>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class WelcomeModal extends React.Component<{}, {}> {
|
||||
totalPages: number = 3;
|
||||
pageNum: OV<number> = mobx.observable.box(1, { name: "welcome-pagenum" });
|
||||
|
||||
@boundMethod
|
||||
closeModal(): void {
|
||||
mobx.action(() => {
|
||||
GlobalModel.welcomeModalOpen.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
goNext(): void {
|
||||
mobx.action(() => {
|
||||
this.pageNum.set(this.pageNum.get() + 1);
|
||||
})();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
goPrev(): void {
|
||||
mobx.action(() => {
|
||||
this.pageNum.set(this.pageNum.get() - 1);
|
||||
})();
|
||||
}
|
||||
|
||||
renderDot(num: number): any {
|
||||
if (num == this.pageNum.get()) {
|
||||
return <i key={String(num)} className="fa-sharp fa-solid fa-circle" />;
|
||||
}
|
||||
return <i key={String(num)} className="fa-sharp fa-regular fa-circle" />;
|
||||
}
|
||||
|
||||
renderDots(): any {
|
||||
let elems: any = [];
|
||||
for (let i = 1; i <= this.totalPages; i++) {
|
||||
let elem = this.renderDot(i);
|
||||
elems.push(elem);
|
||||
}
|
||||
return elems;
|
||||
}
|
||||
|
||||
render() {
|
||||
let pageNum = this.pageNum.get();
|
||||
return (
|
||||
<div className={cn("modal welcome-modal prompt-modal is-active")}>
|
||||
<div className="modal-background" onClick={this.closeModal} />
|
||||
<div className="modal-content">
|
||||
<header>
|
||||
<div className="modal-title">welcome to [prompt]</div>
|
||||
<div className="close-icon hoverEffect" title="Close (Escape)" onClick={this.closeModal}>
|
||||
<XmarkIcon />
|
||||
</div>
|
||||
</header>
|
||||
<div className={cn("inner-content content", { "is-hidden": pageNum != 1 })}>
|
||||
<p>
|
||||
Prompt is a new terminal to help save you time and keep your command-line life organized.
|
||||
Here's a couple quick tips to get your started!
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<If condition={pageNum > 1}>
|
||||
<button className={cn("button is-dark prev-button is-small")} onClick={this.goPrev}>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-regular fa-angle-left" />
|
||||
</span>
|
||||
<span>Prev</span>
|
||||
</button>
|
||||
</If>
|
||||
<If condition={pageNum == 1}>
|
||||
<div className="prev-spacer" />
|
||||
</If>
|
||||
<div className="flex-spacer" />
|
||||
<div className="dots">{this.renderDots()}</div>
|
||||
<div className="flex-spacer" />
|
||||
<If condition={pageNum < this.totalPages}>
|
||||
<button className="button is-dark next-button is-small" onClick={this.goNext}>
|
||||
<span>Next</span>
|
||||
<span className="icon is-small">
|
||||
<i className="fa-sharp fa-regular fa-angle-right" />
|
||||
</span>
|
||||
</button>
|
||||
</If>
|
||||
<If condition={pageNum == this.totalPages}>
|
||||
<button className="button is-dark next-button is-small" onClick={this.closeModal}>
|
||||
<span>Done</span>
|
||||
</button>
|
||||
</If>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class TosModal extends React.Component<{}, {}> {
|
||||
@boundMethod
|
||||
acceptTos(): void {
|
||||
GlobalCommandRunner.clientAcceptTos();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={cn("modal tos-modal wave-modal is-active")}>
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<div className="modal-content-wrapper">
|
||||
<header>
|
||||
<div className="modal-title">Welcome to Wave Terminal!</div>
|
||||
<div className="modal-subtitle">Lets set everything for you</div>
|
||||
</header>
|
||||
<div className="content">
|
||||
<div className="item">
|
||||
<ShieldCheck />
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Telemetry</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<Help />
|
||||
<div className="item-inner"></div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<Github />
|
||||
<div className="item-inner"></div>
|
||||
</div>
|
||||
{/* <p>
|
||||
<a target="_blank" href={util.makeExternLink("https://www.commandline.dev/tos")}>
|
||||
Full Terms of Service
|
||||
</a>
|
||||
</p> */}
|
||||
</div>
|
||||
<footer>
|
||||
<div className="flex-spacer" />
|
||||
<div onClick={this.acceptTos} className="button is-prompt-green is-outlined is-small">
|
||||
Accept Terms of Service
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { WelcomeModal, LoadingSpinner, ClientStopModal, AlertModal, DisconnectedModal, TosModal };
|
8
src/app/assets/icons/shield_check.svg
Normal file
8
src/app/assets/icons/shield_check.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="shield check">
|
||||
<g id="Vector">
|
||||
<path d="M24.3696 6.0192C24.1241 5.98873 23.8759 5.98873 23.6304 6.0192C23.3928 6.0504 23.1432 6.1224 22.2168 6.4176L11.0208 9.9792C10.5168 10.14 10.2288 10.2336 10.0128 10.3176C9.94556 10.3425 9.88053 10.373 9.8184 10.4088C9.74112 10.4722 9.6809 10.5538 9.6432 10.6464C9.62911 10.7176 9.62108 10.7899 9.6192 10.8624C9.60207 11.2149 9.59567 11.5679 9.6 11.9208V25.5768C9.6 30.0336 11.9832 33.6264 15.0288 36.408C18.0768 39.1896 21.6048 40.9848 23.4432 41.808C23.6592 41.904 23.7456 41.9424 23.8128 41.9664C23.934 42.0088 24.066 42.0088 24.1872 41.9664C24.2544 41.9424 24.3432 41.904 24.5568 41.808C26.3952 40.9848 29.9256 39.1896 32.9712 36.408C36.0192 33.6288 38.4 30.036 38.4 25.5792V11.9232C38.4 11.3952 38.4 11.088 38.3832 10.86C38.3806 10.7874 38.3718 10.7151 38.3568 10.644C38.3194 10.5526 38.2601 10.4718 38.184 10.4088C38.1211 10.3729 38.0553 10.3423 37.9872 10.3176C37.6547 10.1954 37.3185 10.0833 36.9792 9.9816L25.7832 6.4176C24.8568 6.1224 24.6072 6.0504 24.3672 6.0192H24.3696ZM23.1888 2.448C23.7275 2.38128 24.2725 2.38128 24.8112 2.448C25.4568 2.5272 26.0736 2.7312 26.8752 2.9856L38.0712 6.5496L38.2632 6.6096C39.0216 6.8496 39.8304 7.104 40.464 7.6224C41.0136 8.0736 41.4384 8.6568 41.7024 9.3168C42.0048 10.0776 42.0024 10.9248 42 11.7216V25.5792C42 31.4352 38.8464 35.9208 35.4 39.0672C31.9488 42.2184 28.0272 44.2032 26.0256 45.096C25.6608 45.2616 25.2624 45.4464 24.7056 45.5424C24.2808 45.6144 23.7192 45.6144 23.2968 45.5424C22.7376 45.4464 22.3392 45.2616 21.9744 45.096C19.9752 44.2032 16.0512 42.2184 12.6024 39.0696C9.1536 35.9208 6 31.4352 6 25.5816V11.7216C5.9976 10.9248 5.9952 10.0776 6.2976 9.3168C6.56036 8.6563 6.98646 8.0733 7.536 7.6224C8.1696 7.104 8.976 6.8496 9.7368 6.6096L9.9288 6.5496L21.1248 2.9856C21.9288 2.7312 22.5432 2.5272 23.1888 2.448Z" fill="#58C142"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.3696 6.0192C24.1241 5.98873 23.8759 5.98873 23.6304 6.0192C23.3928 6.0504 23.1432 6.1224 22.2168 6.4176L11.0208 9.9792C10.5168 10.14 10.2288 10.2336 10.0128 10.3176C9.94556 10.3425 9.88053 10.373 9.8184 10.4088C9.74112 10.4722 9.6809 10.5538 9.6432 10.6464C9.62911 10.7176 9.62108 10.7899 9.6192 10.8624C9.60207 11.2149 9.59567 11.5679 9.6 11.9208V25.5768C9.6 30.0336 11.9832 33.6264 15.0288 36.408C18.0768 39.1896 21.6048 40.9848 23.4432 41.808C23.6592 41.904 23.7456 41.9424 23.8128 41.9664C23.934 42.0088 24.066 42.0088 24.1872 41.9664C24.2544 41.9424 24.3432 41.904 24.5568 41.808C26.3952 40.9848 29.9256 39.1896 32.9712 36.408C36.0192 33.6288 38.4 30.036 38.4 25.5792V11.9232C38.4 11.3952 38.4 11.088 38.3832 10.86C38.3806 10.7874 38.3718 10.7151 38.3568 10.644C38.3194 10.5526 38.2601 10.4718 38.184 10.4088C38.1211 10.3729 38.0553 10.3423 37.9872 10.3176C37.6547 10.1954 37.3185 10.0833 36.9792 9.9816L25.7832 6.4176C24.8568 6.1224 24.6072 6.0504 24.3672 6.0192H24.3696ZM33.436 17.8489C33.3374 17.634 33.1974 17.4407 33.024 17.28C32.8507 17.1192 32.6498 16.9942 32.4281 16.912C32.2064 16.8298 31.9708 16.7921 31.7345 16.8011C31.4983 16.81 31.2661 16.8653 31.0513 16.964C30.8364 17.0626 30.6431 17.2026 30.4824 17.376L21.7704 26.7552L17.52 22.176C17.1917 21.8417 16.7459 21.6486 16.2774 21.6378C15.809 21.627 15.3548 21.7992 15.0113 22.118C14.6679 22.4367 14.4623 22.8769 14.4382 23.3448C14.4141 23.8128 14.5735 24.2717 14.8824 24.624L20.4504 30.624C20.6189 30.8057 20.8231 30.9507 21.0503 31.0499C21.2774 31.1491 21.5226 31.2002 21.7704 31.2002C22.0182 31.2002 22.2634 31.1491 22.4905 31.0499C22.7177 30.9507 22.9219 30.8057 23.0904 30.624L33.12 19.824C33.2808 19.6507 33.4058 19.4474 33.488 19.2257C33.5702 19.004 33.6079 18.7684 33.5989 18.5321C33.59 18.2959 33.5347 18.0637 33.436 17.8489Z" fill="#58C142"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
@ -176,6 +176,77 @@
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
|
||||
input[type="checkbox"] {
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #9e9e9e;
|
||||
transition: color 250ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
}
|
||||
input[type="checkbox"] + label > span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: transparent;
|
||||
border: 2px solid #9e9e9e;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 250ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
}
|
||||
|
||||
input[type="checkbox"] + label:hover,
|
||||
input[type="checkbox"]:focus + label {
|
||||
color: #fff;
|
||||
}
|
||||
input[type="checkbox"] + label:hover > span,
|
||||
input[type="checkbox"]:focus + label > span {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
input[type="checkbox"]:checked + label > ins {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked + label > span {
|
||||
border: 10px solid @term-green;
|
||||
}
|
||||
input[type="checkbox"]:checked + label > span:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: 3px;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
border-right: 2px solid #fff;
|
||||
border-bottom: 2px solid #fff;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 0% 100%;
|
||||
animation: checkbox-check 500ms cubic-bezier(0.4, 0, 0.23, 1);
|
||||
}
|
||||
|
||||
@keyframes checkbox-check {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
33% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button.is-prompt-green {
|
||||
background-color: #222;
|
||||
color: @term-white;
|
||||
@ -186,6 +257,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
.button.is-wave-green {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
padding: 6px 16px !important;
|
||||
color: @term-bright-white !important;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
height: auto !important;
|
||||
background: @term-green !important;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.4), 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.8) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.6) inset;
|
||||
|
||||
&:hover {
|
||||
background-color: @term-green;
|
||||
color: @term-bright-white !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button.is-plain,
|
||||
.button.is-prompt-cancel {
|
||||
background-color: #222;
|
||||
|
@ -99,6 +99,32 @@ class Toggle extends React.Component<{ checked: boolean; onChange: (value: boole
|
||||
}
|
||||
}
|
||||
|
||||
class Checkbox extends React.Component<
|
||||
{ checked: boolean; onChange: (value: boolean) => void; label: string; id: string },
|
||||
{}
|
||||
> {
|
||||
render() {
|
||||
const { checked, onChange, label, id } = this.props;
|
||||
|
||||
return (
|
||||
<div className="checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={id}
|
||||
checked={checked}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
aria-checked={checked}
|
||||
role="checkbox"
|
||||
/>
|
||||
<label htmlFor={id}>
|
||||
<span></span>
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class RemoteStatusLight extends React.Component<{ remote: RemoteType }, {}> {
|
||||
render() {
|
||||
@ -331,6 +357,7 @@ class SettingsError extends React.Component<{ errorMessage: OV<string> }, {}> {
|
||||
export {
|
||||
CmdStrCode,
|
||||
Toggle,
|
||||
Checkbox,
|
||||
renderCmdText,
|
||||
RemoteStatusLight,
|
||||
InlineSettingsTextEdit,
|
||||
|
@ -181,6 +181,109 @@
|
||||
}
|
||||
}
|
||||
|
||||
.modal.wave-modal {
|
||||
.modal-content {
|
||||
display: flex;
|
||||
padding: 32px 48px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
border-radius: 10px;
|
||||
background: var(--olive-dark-1, #151715);
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.35), 0px 10px 24px 0px rgba(0, 0, 0, 0.45),
|
||||
0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset;
|
||||
|
||||
.modal-content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--sizing-sm, 12px);
|
||||
width: 100%;
|
||||
|
||||
.modal-title {
|
||||
color: @term-bright-white;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.modal-subtitle {
|
||||
color: @term-white;
|
||||
text-align: center;
|
||||
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 32px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.item-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
flex: 1 0 0;
|
||||
|
||||
.item-title {
|
||||
color: @term-bright-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
color: @term-white;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.item-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
button.disabled-button {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal.welcome-modal {
|
||||
footer {
|
||||
.prev-button {
|
||||
|
@ -12,9 +12,14 @@ import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import { GlobalModel, GlobalCommandRunner } from "../../../model/model";
|
||||
import { Markdown } from "../common";
|
||||
import * as util from "../../../util/util";
|
||||
import { Toggle, Checkbox } from "../common";
|
||||
import { ClientDataType } from "../../../types/types";
|
||||
|
||||
import { ReactComponent as XmarkIcon } from "../../assets/icons/line/xmark.svg";
|
||||
import { ReactComponent as WarningIcon } from "../../assets/icons/line/triangle-exclamation.svg";
|
||||
import shield from "../../assets/icons/shield_check.svg";
|
||||
import help from "../../assets/icons/help_filled.svg";
|
||||
import github from "../../assets/icons/github.svg";
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
@ -312,48 +317,112 @@ class WelcomeModal extends React.Component<{}, {}> {
|
||||
|
||||
@mobxReact.observer
|
||||
class TosModal extends React.Component<{}, {}> {
|
||||
state = {
|
||||
isChecked: false,
|
||||
};
|
||||
|
||||
@boundMethod
|
||||
handleCheckboxChange(checked: boolean): void {
|
||||
this.setState({ isChecked: checked });
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
acceptTos(): void {
|
||||
GlobalCommandRunner.clientAcceptTos();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleChangeTelemetry(val: boolean): void {
|
||||
if (val) {
|
||||
GlobalCommandRunner.telemetryOn(false);
|
||||
} else {
|
||||
GlobalCommandRunner.telemetryOff(false);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let cdata: ClientDataType = GlobalModel.clientData.get();
|
||||
|
||||
return (
|
||||
<div className={cn("modal tos-modal prompt-modal is-active")}>
|
||||
<div className={cn("modal tos-modal wave-modal is-active")}>
|
||||
<div className="modal-background" />
|
||||
<div className="modal-content">
|
||||
<header>
|
||||
<div className="modal-title">Welcome to [prompt]</div>
|
||||
</header>
|
||||
<div className="inner-content">
|
||||
<div className="modal-content-wrapper">
|
||||
<header>
|
||||
<div className="modal-title">Welcome to Wave Terminal!</div>
|
||||
<div className="modal-subtitle">Lets set everything for you</div>
|
||||
</header>
|
||||
<div className="content">
|
||||
<p>Thank you for downloading Prompt!</p>
|
||||
<p>
|
||||
Prompt is a new terminal designed to help you save time and organize your command life.
|
||||
Prompt is currently in beta. If you'd like to give feedback, run into problems, have
|
||||
questions, or need help, please join the Prompt{" "}
|
||||
<a target="_blank" href={util.makeExternLink("https://discord.gg/XfvZ334gwU")}>
|
||||
discord server
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
Prompt is free to use, no email or registration required (unless you're using the cloud
|
||||
features).
|
||||
</p>
|
||||
<p>
|
||||
<a target="_blank" href={util.makeExternLink("https://www.commandline.dev/tos")}>
|
||||
Full Terms of Service
|
||||
</a>
|
||||
</p>
|
||||
<div className="item">
|
||||
<img src={shield} alt="Privacy" />
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Telemetry</div>
|
||||
<div className="item-text">
|
||||
We don’t collect any personal info, only crash logs and IP address to make Wave
|
||||
better. If you like, you can disable telemetry now or late.
|
||||
</div>
|
||||
<div className="item-field">
|
||||
<Toggle
|
||||
checked={!cdata.clientopts.notelemetry}
|
||||
onChange={this.handleChangeTelemetry}
|
||||
/>
|
||||
<div className="item-label">Basic Telemetry</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<img src={help} alt="Help" />
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Help</div>
|
||||
<div className="item-text">
|
||||
If you need any help or you have feature request, you can join{" "}
|
||||
<a target="_blank" href={util.makeExternLink("https://discord.gg/XfvZ334gwU")}>
|
||||
our Discord
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item">
|
||||
<img src={github} alt="Github" />
|
||||
<div className="item-inner">
|
||||
<div className="item-title">Like Wave? Give us a star</div>
|
||||
<div className="item-text">
|
||||
Rankings are very important for small startups like us, it helps other people to
|
||||
know about us. If you like Wave, please consider giving us a star on our{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
href={util.makeExternLink("https://github.com/wavetermdev/waveterm")}
|
||||
>
|
||||
Github Repository
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div>
|
||||
<Checkbox
|
||||
checked={this.state.isChecked}
|
||||
label="I accept the Terms of Service"
|
||||
id="accept-tos"
|
||||
onChange={this.handleCheckboxChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="button-wrapper">
|
||||
<button
|
||||
onClick={this.acceptTos}
|
||||
className={cn("button is-wave-green is-outlined is-small", {
|
||||
"disabled-button": !this.state.isChecked,
|
||||
})}
|
||||
disabled={!this.state.isChecked}
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<footer>
|
||||
<div className="flex-spacer" />
|
||||
<div onClick={this.acceptTos} className="button is-prompt-green is-outlined is-small">
|
||||
Accept Terms of Service
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -3677,12 +3677,13 @@ func setNoTelemetry(ctx context.Context, clientData *sstore.ClientData, noTeleme
|
||||
return fmt.Errorf("error trying to update client telemetry: %v", err)
|
||||
}
|
||||
log.Printf("client no-telemetry setting updated to %v\n", noTelemetryVal)
|
||||
err = pcloud.SendNoTelemetryUpdate(ctx, clientOpts.NoTelemetry)
|
||||
if err != nil {
|
||||
// ignore error, just log
|
||||
log.Printf("[error] sending no-telemetry update: %v\n", err)
|
||||
log.Printf("note that telemetry update has still taken effect locally, and will be respected by the client\n")
|
||||
}
|
||||
go func() {
|
||||
err := pcloud.SendNoTelemetryUpdate(ctx, clientOpts.NoTelemetry)
|
||||
if err != nil {
|
||||
log.Printf("[error] sending no-telemetry update: %v\n", err)
|
||||
log.Printf("note that telemetry update has still taken effect locally, and will be respected by the client\n")
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -3698,11 +3699,13 @@ func TelemetryOnCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = pcloud.SendTelemetry(ctx, false)
|
||||
if err != nil {
|
||||
// ignore error, but log
|
||||
log.Printf("[error] sending telemetry update (in /telemetry:on): %v\n", err)
|
||||
}
|
||||
go func() {
|
||||
err := pcloud.SendTelemetry(ctx, false)
|
||||
if err != nil {
|
||||
// ignore error, but log
|
||||
log.Printf("[error] sending telemetry update (in /telemetry:on): %v\n", err)
|
||||
}
|
||||
}()
|
||||
clientData, err = sstore.EnsureClientData(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot retrieve updated client data: %v", err)
|
||||
|
Loading…
Reference in New Issue
Block a user