mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Add tutorial popovers
This commit is contained in:
@@ -9,9 +9,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
|
||||
PREFERENCE_CATEGORY_TUTORIAL_INTRO_COMPLETE = "tutorial_intro_complete"
|
||||
PREFERENCE_CATEGORY_TUTORIAL_POPOVERS = "tutorial_popovers"
|
||||
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
|
||||
)
|
||||
|
||||
type Preference struct {
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||
const Client = require('../utils/client.jsx');
|
||||
const AsyncClient = require('../utils/async_client.jsx');
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const PostStore = require('../stores/post_store.jsx');
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const SocketStore = require('../stores/socket_store.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const MsgTyping = require('./msg_typing.jsx');
|
||||
const Textbox = require('./textbox.jsx');
|
||||
const FileUpload = require('./file_upload.jsx');
|
||||
const FilePreview = require('./file_preview.jsx');
|
||||
const TutorialTip = require('./tutorial/tutorial_tip.jsx');
|
||||
|
||||
const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||
const Client = require('../utils/client.jsx');
|
||||
const AsyncClient = require('../utils/async_client.jsx');
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const PostStore = require('../stores/post_store.jsx');
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const SocketStore = require('../stores/socket_store.jsx');
|
||||
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const Preferences = Constants.Preferences;
|
||||
const TutorialSteps = Constants.TutorialSteps;
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
@@ -36,15 +41,16 @@ export default class CreatePost extends React.Component {
|
||||
this.handleTextDrop = this.handleTextDrop.bind(this);
|
||||
this.removePreview = this.removePreview.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
||||
this.getFileCount = this.getFileCount.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
this.sendMessage = this.sendMessage.bind(this);
|
||||
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
||||
|
||||
PostStore.clearDraftUploads();
|
||||
|
||||
const draft = this.getCurrentDraft();
|
||||
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
|
||||
|
||||
this.state = {
|
||||
channelId: ChannelStore.getCurrentId(),
|
||||
@@ -55,16 +61,12 @@ export default class CreatePost extends React.Component {
|
||||
initialText: draft.messageText,
|
||||
windowWidth: Utils.windowWidth(),
|
||||
windowHeight: Utils.windowHeight(),
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value,
|
||||
showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.POST_POPOVER
|
||||
};
|
||||
|
||||
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
||||
}
|
||||
onPreferenceChange() {
|
||||
this.setState({
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
});
|
||||
}
|
||||
handleResize() {
|
||||
this.setState({
|
||||
windowWidth: Utils.windowWidth(),
|
||||
@@ -318,11 +320,13 @@ export default class CreatePost extends React.Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
||||
this.resizePostHolder();
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
PreferenceStore.removeChangeListener(this.onPreferenceChange);
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
onChange() {
|
||||
@@ -333,6 +337,13 @@ export default class CreatePost extends React.Component {
|
||||
this.setState({channelId, messageText: draft.messageText, initialText: draft.messageText, submitting: false, serverError: null, postError: null, previews: draft.previews, uploadsInProgress: draft.uploadsInProgress});
|
||||
}
|
||||
}
|
||||
onPreferenceChange() {
|
||||
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
|
||||
this.setState({
|
||||
showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.POST_POPOVER,
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
});
|
||||
}
|
||||
getFileCount(channelId) {
|
||||
if (channelId === this.state.channelId) {
|
||||
return this.state.previews.length + this.state.uploadsInProgress.length;
|
||||
@@ -367,6 +378,25 @@ export default class CreatePost extends React.Component {
|
||||
});
|
||||
}
|
||||
}
|
||||
createTutorialTip() {
|
||||
const screens = [];
|
||||
|
||||
screens.push(
|
||||
<div>
|
||||
<h4><strong>{'Sending Messages'}</strong></h4>
|
||||
{'Type here to write a message.'}
|
||||
<br/><br/>
|
||||
{'Click the attachment button to upload an image or a file.'}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<TutorialTip
|
||||
placement='top'
|
||||
screens={screens}
|
||||
/>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
let serverError = null;
|
||||
if (this.state.serverError) {
|
||||
@@ -398,6 +428,11 @@ export default class CreatePost extends React.Component {
|
||||
postFooterClassName += ' has-error';
|
||||
}
|
||||
|
||||
let tutorialTip = null;
|
||||
if (this.state.showTutorialTip) {
|
||||
tutorialTip = this.createTutorialTip();
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
id='create_post'
|
||||
@@ -436,6 +471,7 @@ export default class CreatePost extends React.Component {
|
||||
>
|
||||
<i className='fa fa-paper-plane' />
|
||||
</a>
|
||||
{tutorialTip}
|
||||
</div>
|
||||
<div className={postFooterClassName}>
|
||||
{postError}
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const AsyncClient = require('../utils/async_client.jsx');
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const Client = require('../utils/client.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const NewChannelFlow = require('./new_channel_flow.jsx');
|
||||
const MoreDirectChannels = require('./more_direct_channels.jsx');
|
||||
const SearchBox = require('./search_bar.jsx');
|
||||
const SidebarHeader = require('./sidebar_header.jsx');
|
||||
const TeamStore = require('../stores/team_store.jsx');
|
||||
const UnreadChannelIndicator = require('./unread_channel_indicator.jsx');
|
||||
const TutorialTip = require('./tutorial/tutorial_tip.jsx');
|
||||
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const TeamStore = require('../stores/team_store.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
|
||||
const AsyncClient = require('../utils/async_client.jsx');
|
||||
const Client = require('../utils/client.jsx');
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const Preferences = Constants.Preferences;
|
||||
const TutorialSteps = Constants.TutorialSteps;
|
||||
|
||||
const Tooltip = ReactBootstrap.Tooltip;
|
||||
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
|
||||
@@ -155,12 +162,15 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
visibleDirectChannels.sort(this.sortChannelsByDisplayName);
|
||||
|
||||
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
|
||||
|
||||
return {
|
||||
activeId: currentId,
|
||||
channels: ChannelStore.getAll(),
|
||||
members,
|
||||
visibleDirectChannels,
|
||||
hiddenDirectChannelCount
|
||||
hiddenDirectChannelCount,
|
||||
showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.CHANNEL_POPOVER
|
||||
};
|
||||
}
|
||||
|
||||
@@ -308,6 +318,44 @@ export default class Sidebar extends React.Component {
|
||||
this.setState({showDirectChannelsModal: false});
|
||||
}
|
||||
|
||||
createTutorialTip() {
|
||||
const screens = [];
|
||||
|
||||
screens.push(
|
||||
<div>
|
||||
<h4><strong>{'Channels'}</strong></h4>
|
||||
<strong>{'Channels'}</strong>{' organize conversations across different topics. They’re open to everyone on your team. To send private communications use '}<strong>{'Direct Messages'}</strong>{' for a single person or '}<strong>{'Private Groups'}</strong>{' for multiple people.'}
|
||||
</div>
|
||||
);
|
||||
|
||||
screens.push(
|
||||
<div>
|
||||
<h4><strong>{'"Town Square" and "Off-Topic" channels'}</strong></h4>
|
||||
{'Here are two public channels to start:'}
|
||||
<br/><br/>
|
||||
<strong>{'Town Square'}</strong>{' is a place for team-wide communication. Everyone in your team is a member of this channel.'}
|
||||
<br/><br/>
|
||||
<strong>{'Off-Topic'}</strong>{' is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.'}
|
||||
</div>
|
||||
);
|
||||
|
||||
screens.push(
|
||||
<div>
|
||||
<h4><strong>{'Creating and Joining Channels'}</strong></h4>
|
||||
{'Click '}<strong>{'"More..."'}</strong>{' to create a new channel or join an existing one.'}
|
||||
<br/><br/>
|
||||
{'You can also create a new channel or private group by clicking the '}<strong>{'"+" symbol'}</strong>{' next to the channel or private group header.'}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<TutorialTip
|
||||
placement='right'
|
||||
screens={screens}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
createChannelElement(channel, index, arr, handleClose) {
|
||||
var members = this.state.members;
|
||||
var activeId = this.state.activeId;
|
||||
@@ -444,6 +492,11 @@ export default class Sidebar extends React.Component {
|
||||
rowClass += ' has-close';
|
||||
}
|
||||
|
||||
let tutorialTip = null;
|
||||
if (this.state.showTutorialTip && channel.name === Constants.DEFAULT_CHANNEL) {
|
||||
tutorialTip = this.createTutorialTip();
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
key={channel.name}
|
||||
@@ -460,6 +513,7 @@ export default class Sidebar extends React.Component {
|
||||
{badge}
|
||||
{closeButton}
|
||||
</a>
|
||||
{tutorialTip}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var NavbarDropdown = require('./navbar_dropdown.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
const NavbarDropdown = require('./navbar_dropdown.jsx');
|
||||
const TutorialTip = require('./tutorial/tutorial_tip.jsx');
|
||||
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const Preferences = Constants.Preferences;
|
||||
const TutorialSteps = Constants.TutorialSteps;
|
||||
|
||||
const Tooltip = ReactBootstrap.Tooltip;
|
||||
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
@@ -13,8 +20,23 @@ export default class SidebarHeader extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.toggleDropdown = this.toggleDropdown.bind(this);
|
||||
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
||||
|
||||
this.state = {};
|
||||
this.state = this.getStateFromStores();
|
||||
}
|
||||
componentDidMount() {
|
||||
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
PreferenceStore.removeChangeListener(this.onPreferenceChange);
|
||||
}
|
||||
getStateFromStores() {
|
||||
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
|
||||
|
||||
return {showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.MENU_POPOVER};
|
||||
}
|
||||
onPreferenceChange() {
|
||||
this.setState(this.getStateFromStores());
|
||||
}
|
||||
toggleDropdown(e) {
|
||||
e.preventDefault();
|
||||
@@ -22,8 +44,63 @@ export default class SidebarHeader extends React.Component {
|
||||
this.refs.dropdown.blockToggle = false;
|
||||
return;
|
||||
}
|
||||
console.log(this.refs.tip);
|
||||
this.refs.tip.toggle();
|
||||
$('.team__header').find('.dropdown-toggle').dropdown('toggle');
|
||||
}
|
||||
createTutorialTip() {
|
||||
const screens = [];
|
||||
|
||||
let teamSettingsLink = <strong>{'Team Settings'}</strong>;
|
||||
if (Utils.isAdmin(UserStore.getCurrentUser().roles)) {
|
||||
teamSettingsLink = (
|
||||
<a
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#team_settings'
|
||||
>
|
||||
{'Team Settings'}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
screens.push(
|
||||
<div>
|
||||
<h4><strong>{'Sending Messages'}</strong></h4>
|
||||
{'The '}<strong>{'Main Menu'}</strong>{' is where you can '}
|
||||
<a
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#invite_member'
|
||||
>
|
||||
{'Invite New Members'}
|
||||
</a>
|
||||
{', access your '}
|
||||
<a
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#user_settings'
|
||||
>
|
||||
{'Account Settings'}
|
||||
</a>
|
||||
{', and set your '}<strong>{'Theme Color'}</strong>{'.'}
|
||||
<br/><br/>
|
||||
{'Team administrators can also access their '}{teamSettingsLink}{' from this menu.'}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={this.toggleDropdown}
|
||||
>
|
||||
<TutorialTip
|
||||
ref='tip'
|
||||
placement='right'
|
||||
screens={screens}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
var me = UserStore.getCurrentUser();
|
||||
var profilePicture = null;
|
||||
@@ -41,8 +118,14 @@ export default class SidebarHeader extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let tutorialTip = null;
|
||||
if (this.state.showTutorialTip) {
|
||||
tutorialTip = this.createTutorialTip();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='team__header theme'>
|
||||
{tutorialTip}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.toggleDropdown}
|
||||
|
||||
@@ -18,21 +18,25 @@ export default class TutorialIntroScreens extends React.Component {
|
||||
this.handleNext = this.handleNext.bind(this);
|
||||
this.createScreen = this.createScreen.bind(this);
|
||||
|
||||
this.state = {screen: 0};
|
||||
this.state = {currentScreen: 0};
|
||||
}
|
||||
handleNext() {
|
||||
if (this.state.screen < 2) {
|
||||
this.setState({screen: this.state.screen + 1});
|
||||
if (this.state.currentScreen < 2) {
|
||||
this.setState({currentScreen: this.state.currentScreen + 1});
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
|
||||
|
||||
const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_INTRO_COMPLETE, UserStore.getCurrentId(), 'true');
|
||||
let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
|
||||
|
||||
const newValue = (parseInt(preference.value, 10) + 1).toString();
|
||||
|
||||
preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
|
||||
AsyncClient.savePreferences([preference]);
|
||||
}
|
||||
createScreen() {
|
||||
switch (this.state.screen) {
|
||||
switch (this.state.currentScreen) {
|
||||
case 0:
|
||||
return this.createScreenOne();
|
||||
case 1:
|
||||
|
||||
108
web/react/components/tutorial/tutorial_tip.jsx
Normal file
108
web/react/components/tutorial/tutorial_tip.jsx
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const UserStore = require('../../stores/user_store.jsx');
|
||||
const PreferenceStore = require('../../stores/preference_store.jsx');
|
||||
const AsyncClient = require('../../utils/async_client.jsx');
|
||||
|
||||
const Constants = require('../../utils/constants.jsx');
|
||||
const Preferences = Constants.Preferences;
|
||||
|
||||
const Overlay = ReactBootstrap.Overlay;
|
||||
|
||||
export default class TutorialTip extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleNext = this.handleNext.bind(this);
|
||||
this.toggle = this.toggle.bind(this);
|
||||
|
||||
this.state = {currentScreen: 0, show: false};
|
||||
}
|
||||
toggle() {
|
||||
const show = !this.state.show;
|
||||
this.setState({show});
|
||||
|
||||
if (!show && this.state.currentScreen >= this.props.screens.length - 1) {
|
||||
let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
|
||||
|
||||
const newValue = (parseInt(preference.value, 10) + 1).toString();
|
||||
|
||||
preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
|
||||
AsyncClient.savePreferences([preference]);
|
||||
}
|
||||
}
|
||||
handleNext() {
|
||||
if (this.state.currentScreen < this.props.screens.length - 1) {
|
||||
this.setState({currentScreen: this.state.currentScreen + 1});
|
||||
return;
|
||||
}
|
||||
|
||||
this.toggle();
|
||||
}
|
||||
skipTutorial(e) {
|
||||
e.preventDefault();
|
||||
const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
|
||||
AsyncClient.savePreferences([preference]);
|
||||
}
|
||||
render() {
|
||||
const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? 'Okay' : 'Next';
|
||||
|
||||
const dots = [];
|
||||
if (this.props.screens.length > 1) {
|
||||
for (let i = 0; i < this.props.screens.length; i++) {
|
||||
if (i === this.state.currentScreen) {
|
||||
dots.push(<span key={'dotactive' + i}>{'[ x ]'}</span>);
|
||||
} else {
|
||||
dots.push(<span key={'dotinactive' + i}>{'[ ]'}</span>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='tip-div'>
|
||||
<img
|
||||
className='tip-button'
|
||||
src='/static/images/next.png'
|
||||
onClick={this.toggle}
|
||||
ref='target'
|
||||
/>
|
||||
|
||||
<Overlay
|
||||
placement={this.props.placement}
|
||||
show={this.state.show}
|
||||
rootClose={true}
|
||||
onHide={this.toggle}
|
||||
target={() => this.refs.target}
|
||||
>
|
||||
<div className='tip-overlay'>
|
||||
{this.props.screens[this.state.currentScreen]}
|
||||
<br/>
|
||||
{dots}
|
||||
<button
|
||||
className='btn btn-default'
|
||||
onClick={this.handleNext}
|
||||
>
|
||||
{buttonText}
|
||||
</button>
|
||||
<br/>
|
||||
<span>
|
||||
{'Seen this before? '}
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.skipTutorial}
|
||||
>
|
||||
{'Opt out of these tips.'}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TutorialTip.propTypes = {
|
||||
screens: React.PropTypes.array.isRequired,
|
||||
placement: React.PropTypes.string.isRequired
|
||||
};
|
||||
@@ -314,9 +314,14 @@ module.exports = {
|
||||
Preferences: {
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings'
|
||||
TUTORIAL_INTRO_COMPLETE: 'tutorial_intro_complete',
|
||||
TUTORIAL_POPOVERS: 'tutorial_popovers'
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
|
||||
TUTORIAL_STEP: 'tutorial_step'
|
||||
},
|
||||
TutorialSteps: {
|
||||
INTRO_SCREENS: 0,
|
||||
POST_POPOVER: 1,
|
||||
CHANNEL_POPOVER: 2,
|
||||
MENU_POPOVER: 3
|
||||
},
|
||||
KeyCodes: {
|
||||
UP: 38,
|
||||
|
||||
21
web/sass-files/sass/partials/_tutorial.scss
Normal file
21
web/sass-files/sass/partials/_tutorial.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
.tip-overlay {
|
||||
position:absolute;
|
||||
background-color:#EEE;
|
||||
box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
border:1px solid #CCC;
|
||||
border-radius:3px;
|
||||
padding:10px;
|
||||
z-index:999;
|
||||
}
|
||||
|
||||
.tip-button {
|
||||
height:20px;
|
||||
width:20px;
|
||||
z-index:998;
|
||||
}
|
||||
|
||||
.tip-div {
|
||||
position:absolute;
|
||||
top:0px;
|
||||
right:0px;
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
@import "partials/loading";
|
||||
@import "partials/get-link";
|
||||
@import "partials/markdown";
|
||||
@import "partials/statistics";
|
||||
@import "partials/tutorial";
|
||||
|
||||
// Responsive Css
|
||||
@import "partials/responsive";
|
||||
|
||||
Reference in New Issue
Block a user