Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 125 additions & 97 deletions packages/background-tips/lib/background-tips-view.js
Original file line number Diff line number Diff line change
@@ -1,156 +1,184 @@
const _ = require('underscore-plus')
const {CompositeDisposable, Disposable} = require('atom')
const Tips = require('./tips')
const _ = require("underscore-plus");
const { CompositeDisposable, Disposable } = require("atom");

const TEMPLATE = `\
<ul class="centered background-message">
<li class="message"></li>
</ul>\
`

module.exports =
class BackgroundTipsElement {
constructor () {
this.element = document.createElement('background-tips')
this.index = -1
this.workspaceCenter = atom.workspace.getCenter()

this.startDelay = 1000
this.displayDuration = 10000
this.fadeDuration = 300

this.disposables = new CompositeDisposable()

const visibilityCallback = () => this.updateVisibility()

this.disposables.add(this.workspaceCenter.onDidAddPane(visibilityCallback))
this.disposables.add(this.workspaceCenter.onDidDestroyPane(visibilityCallback))
this.disposables.add(this.workspaceCenter.onDidChangeActivePaneItem(visibilityCallback))

atom.getCurrentWindow().on('blur', visibilityCallback)
atom.getCurrentWindow().on('focus', visibilityCallback)

this.disposables.add(new Disposable(() => atom.getCurrentWindow().removeListener('blur', visibilityCallback)))
this.disposables.add(new Disposable(() => atom.getCurrentWindow().removeListener('focus', visibilityCallback)))

this.startTimeout = setTimeout(() => this.start(), this.startDelay)
`;

module.exports = class BackgroundTipsElement {
constructor(main) {
this.main = main;
this.element = document.createElement("background-tips");
this.index = -1;
this.workspaceCenter = atom.workspace.getCenter();

this.startDelay = 1000;
this.displayDuration = 10000;
this.fadeDuration = 300;

this.renderedTips = [];
this.tipsRendered = false;

this.disposables = new CompositeDisposable();

const visibilityCallback = () => this.updateVisibility();

this.disposables.add(this.workspaceCenter.onDidAddPane(visibilityCallback));
this.disposables.add(
this.workspaceCenter.onDidDestroyPane(visibilityCallback)
);
this.disposables.add(
this.workspaceCenter.onDidChangeActivePaneItem(visibilityCallback)
);

atom.getCurrentWindow().on("blur", visibilityCallback);
atom.getCurrentWindow().on("focus", visibilityCallback);

this.disposables.add(
new Disposable(() =>
atom.getCurrentWindow().removeListener("blur", visibilityCallback)
)
);
this.disposables.add(
new Disposable(() =>
atom.getCurrentWindow().removeListener("focus", visibilityCallback)
)
);

this.startTimeout = setTimeout(() => this.start(), this.startDelay);
}

destroy () {
this.stop()
this.disposables.dispose()
destroy() {
this.stop();
this.disposables.dispose();
}

attach () {
this.element.innerHTML = TEMPLATE
this.message = this.element.querySelector('.message')
attach() {
this.element.innerHTML = TEMPLATE;
this.message = this.element.querySelector(".message");

const paneView = atom.views.getView(this.workspaceCenter.getActivePane())
const itemViews = paneView.querySelector('.item-views')
let top = 0
const paneView = atom.views.getView(this.workspaceCenter.getActivePane());
const itemViews = paneView.querySelector(".item-views");
let top = 0;
if (itemViews && itemViews.offsetTop) {
top = itemViews.offsetTop
top = itemViews.offsetTop;
}

this.element.style.top = top + 'px'
paneView.appendChild(this.element)
this.element.style.top = top + "px";
paneView.appendChild(this.element);
}

detach () {
this.element.remove()
detach() {
this.element.remove();
}

updateVisibility () {
updateVisibility() {
if (this.shouldBeAttached()) {
this.start()
this.start();
} else {
this.stop()
this.stop();
}
}

shouldBeAttached () {
return this.workspaceCenter.getPanes().length === 1 &&
this.workspaceCenter.getActivePaneItem() == null &&
atom.getCurrentWindow().isFocused()
shouldBeAttached() {
return (
this.workspaceCenter.getPanes().length === 1 &&
this.workspaceCenter.getActivePaneItem() == null &&
atom.getCurrentWindow().isFocused()
);
}

start () {
if (!this.shouldBeAttached() || this.interval != null) return
this.renderTips()
this.randomizeIndex()
this.attach()
this.showNextTip()
this.interval = setInterval(() => this.showNextTip(), this.displayDuration)
start() {
if (!this.shouldBeAttached() || this.interval != null) return;
this.renderTips();
this.randomizeIndex();
this.attach();
this.showNextTip();
this.interval = setInterval(() => this.showNextTip(), this.displayDuration);
}

stop () {
this.element.remove()
stop() {
this.element.remove();
if (this.interval != null) {
clearInterval(this.interval)
clearInterval(this.interval);
}

clearTimeout(this.startTimeout)
clearTimeout(this.nextTipTimeout)
this.interval = null
clearTimeout(this.startTimeout);
clearTimeout(this.nextTipTimeout);
this.interval = null;
}

randomizeIndex () {
const len = Tips.length
this.index = Math.round(Math.random() * len) % len
randomizeIndex() {
const len = this.renderedTips.length;
this.index = len > 0 ? Math.round(Math.random() * len) % len : 0;
}

showNextTip () {
this.index = ++this.index % Tips.length
this.message.classList.remove('fade-in')
showNextTip() {
if (this.renderedTips.length === 0) return;
this.index = ++this.index % this.renderedTips.length;
this.message.classList.remove("fade-in");
this.nextTipTimeout = setTimeout(() => {
this.message.innerHTML = Tips[this.index]
this.message.classList.add('fade-in')
}, this.fadeDuration)
this.message.innerHTML = this.renderedTips[this.index];
this.message.classList.add("fade-in");
}, this.fadeDuration);
}

renderTips () {
if (this.tipsRendered) return
for (let i = 0; i < Tips.length; i++) {
const tip = Tips[i]
Tips[i] = this.renderTip(tip)
renderTips() {
if (this.tipsRendered) return;
const tips = this.main.getTips();
this.renderedTips = tips.map((tip) => this.renderTip(tip));
this.tipsRendered = true;
}

tipsChanged() {
this.tipsRendered = false;
if (this.interval != null) {
this.renderTips();
this.index = Math.min(this.index, this.renderedTips.length - 1);
}
this.tipsRendered = true
}

renderTip (str) {
renderTip(str) {
str = str.replace(/\{(.+)\}/g, (match, command) => {
let binding, scope
const scopeAndCommand = command.split('>')
let binding, scope;
const scopeAndCommand = command.split(">");
if (scopeAndCommand.length > 1) {
[scope, command] = scopeAndCommand
[scope, command] = scopeAndCommand;
}
const bindings = atom.keymaps.findKeyBindings({command: command.trim()})
const bindings = atom.keymaps.findKeyBindings({
command: command.trim(),
});

if (scope) {
for (binding of bindings) {
if (binding.selector === scope) break
if (binding.selector === scope) break;
}
} else {
binding = this.getKeyBindingForCurrentPlatform(bindings)
binding = this.getKeyBindingForCurrentPlatform(bindings);
}

if (binding && binding.keystrokes) {
const keystrokeLabel = _.humanizeKeystroke(binding.keystrokes).replace(/\s+/g, '&nbsp')
return `<span class="keystroke">${keystrokeLabel}</span>`
const keystrokeLabel = _.humanizeKeystroke(binding.keystrokes).replace(
/\s+/g,
"&nbsp"
);
return `<span class="keystroke">${keystrokeLabel}</span>`;
} else {
return command
return command;
}
})
return str
});
return str;
}

getKeyBindingForCurrentPlatform (bindings) {
if (!bindings || !bindings.length) return
getKeyBindingForCurrentPlatform(bindings) {
if (!bindings || !bindings.length) return;
for (let binding of bindings) {
if (binding.selector.indexOf(process.platform) !== -1) {
return binding
return binding;
}
}
return bindings[0]
return bindings[0];
}
}
};
39 changes: 32 additions & 7 deletions packages/background-tips/lib/background-tips.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
const BackgroundTipsView = require('./background-tips-view')
const { Disposable } = require("atom");
const BackgroundTipsView = require("./background-tips-view");

module.exports = {
activate () {
this.backgroundTipsView = new BackgroundTipsView()
activate() {
this.defaultTips = require("./tips");
this.addedTips = new Set();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't test this at the moment. But with storing all acquired tips in a Set what are the chances we would retain tips after a package has been disabled in our current session? If this is still an issue, we could consider that still not even loading them after a reload is a huge improvement from previous behavior, but was just curious considering the goals of this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, the service is too simple now. I will prepare new commits today that will handle lifecycle of tips better.

this.backgroundTipsView = new BackgroundTipsView(this);
},

deactivate () {
this.backgroundTipsView.destroy()
}
}
deactivate() {
this.backgroundTipsView.destroy();
this.addedTips.clear();
},

getTips() {
let all = [...this.defaultTips];
for (const tips of this.addedTips) {
all = all.concat(tips);
}
return all;
},

provideBackgroundTips() {
return {
addTips: (tips) => {
this.addedTips.add(tips);
this.backgroundTipsView.tipsChanged();
return new Disposable(() => {
this.addedTips.delete(tips);
this.backgroundTipsView.tipsChanged();
});
},
};
},
};
17 changes: 4 additions & 13 deletions packages/background-tips/lib/tips.js
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it might be somewhat antithetical to this PR, but we may want to leave the github package based tips in here until we are able to actually migrate that package into core, which we are still working towards and tracking in #512

Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
module.exports = [
'Close panels like find and replace with {body>core:cancel}',
`Everything ${atom.branding.name} can do is in the Command Palette. See it by using {command-palette:toggle}`,
'You can quickly open files with the Fuzzy Finder. Try it by using {fuzzy-finder:toggle-file-finder}',
'You can toggle the Tree View with {tree-view:toggle}',
'You can focus the Tree View with {tree-view:toggle-focus}',
'You can toggle the Git tab with {github:toggle-git-tab}',
'You can focus the Git tab with {github:toggle-git-tab-focus}',
'You can toggle the GitHub tab with {github:toggle-github-tab}',
'You can focus the GitHub tab with {github:toggle-github-tab-focus}',
'You can split a pane with {pane:split-right-and-copy-active-item}',
'You can jump to a method in the editor using {symbols-view:toggle-file-symbols}',
'You can install packages and themes from the Settings View {settings-view:open}'
]
"Split your editor into multiple panes with {pane:split-right-and-copy-active-item}",
"You can move lines of text up and down with {editor:move-line-up} and {editor:move-line-down}",
"Toggle line comments quickly with {editor:toggle-line-comments}",
];
8 changes: 8 additions & 0 deletions packages/background-tips/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
"engines": {
"atom": ">0.42.0"
},
"providedServices": {
"background-tips": {
"description": "Allows packages to register tips shown when no editors are open",
"versions": {
"1.0.0": "provideBackgroundTips"
}
}
},
"dependencies": {
"underscore-plus": "1.x"
}
Expand Down
Loading
Loading