mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
Fix onebox loading on every keystroke after a request fails.
This commit is contained in:
parent
016634d1d9
commit
e4277757c4
5 changed files with 158 additions and 89 deletions
|
@ -1,89 +1,106 @@
|
||||||
/**
|
/**
|
||||||
A helper for looking up oneboxes and displaying them
|
A helper for looking up oneboxes and displaying them
|
||||||
|
|
||||||
For now it only stores in a var, in future we can change it so it uses localStorage.
|
For now it only stores in a local Javascript Object, in future we can change it so it uses localStorage
|
||||||
|
or some other mechanism.
|
||||||
|
|
||||||
@class Notification
|
@class Onebox
|
||||||
@extends Discourse.Model
|
|
||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.Onebox = (function() {
|
Discourse.Onebox = {
|
||||||
|
|
||||||
var cache, load, localCache, lookup, lookupCache;
|
// The cache is just a JS Object
|
||||||
localCache = {};
|
localCache: {},
|
||||||
|
|
||||||
cache = function(url, contents) {
|
// A cache of failed URLs
|
||||||
localCache[url] = contents;
|
failedCache: {},
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
lookupCache = function(url) {
|
/**
|
||||||
var cached;
|
Perform a lookup of a onebox based an anchor element. It will insert a loading
|
||||||
cached = localCache[url];
|
indicator and remove it when the loading is complete or fails.
|
||||||
if (cached && cached.then) {
|
|
||||||
return null;
|
@method load
|
||||||
} else {
|
@param {HTMLElement} e the anchor element whose onebox we want to look up
|
||||||
return cached;
|
@param {Boolean} refresh true if we want to force a refresh of the onebox
|
||||||
|
**/
|
||||||
|
load: function(e, refresh) {
|
||||||
|
|
||||||
|
var $elem = $(e);
|
||||||
|
|
||||||
|
// If the onebox has loaded, return
|
||||||
|
if ($elem.data('onebox-loaded')) return;
|
||||||
|
if ($elem.hasClass('loading-onebox')) return;
|
||||||
|
|
||||||
|
var url = e.href;
|
||||||
|
|
||||||
|
// Unless we're forcing a refresh...
|
||||||
|
if (!refresh) {
|
||||||
|
// If we have it in our cache, return it.
|
||||||
|
var cached = this.localCache[url];
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
// If the request failed, don't do anything
|
||||||
|
var failed = this.failedCache[url];
|
||||||
|
if (failed) return;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
lookup = function(url, refresh, callback) {
|
// Add the loading CSS class
|
||||||
var cached;
|
$elem.addClass('loading-onebox');
|
||||||
cached = localCache[url];
|
|
||||||
if (refresh && cached && !cached.then) {
|
|
||||||
cached = null;
|
|
||||||
}
|
|
||||||
if (cached) {
|
|
||||||
if (cached.then) {
|
|
||||||
cached.then(callback(lookupCache(url)));
|
|
||||||
} else {
|
|
||||||
callback(cached);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
cache(url, jQuery.get("/onebox", {
|
|
||||||
url: url,
|
|
||||||
refresh: refresh
|
|
||||||
}, function(html) {
|
|
||||||
cache(url, html);
|
|
||||||
return callback(html);
|
|
||||||
}));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
load = function(e, refresh) {
|
// Retrieve the onebox
|
||||||
var $elem, loading, url;
|
var promise = $.ajax({
|
||||||
if (!refresh) refresh = false;
|
type: 'GET',
|
||||||
|
url: "/onebox",
|
||||||
|
data: { url: url, refresh: refresh }
|
||||||
|
});
|
||||||
|
|
||||||
url = e.href;
|
// We can call this when loading is complete
|
||||||
$elem = $(e);
|
var loadingFinished = function() {
|
||||||
if ($elem.data('onebox-loaded')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loading = lookup(url, refresh, function(html) {
|
|
||||||
$elem.removeClass('loading-onebox');
|
$elem.removeClass('loading-onebox');
|
||||||
$elem.data('onebox-loaded');
|
$elem.data('onebox-loaded');
|
||||||
if (!html) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (html.trim().length === 0) {
|
|
||||||
return;
|
var onebox = this;
|
||||||
}
|
promise.then(function(html) {
|
||||||
return $elem.replaceWith(html);
|
|
||||||
|
// successfully loaded onebox
|
||||||
|
loadingFinished();
|
||||||
|
|
||||||
|
onebox.localCache[url] = html;
|
||||||
|
$elem.replaceWith(html);
|
||||||
|
|
||||||
|
}, function() {
|
||||||
|
// If the request failed log it as such
|
||||||
|
onebox.failedCache[url] = true;
|
||||||
|
loadingFinished();
|
||||||
});
|
});
|
||||||
if (loading) {
|
|
||||||
return $elem.addClass('loading-onebox');
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the cached contents of a Onebox
|
||||||
|
|
||||||
|
@method lookupCache
|
||||||
|
@param {String} url the url of the onebox
|
||||||
|
@return {String} the cached contents of the onebox or null if not found
|
||||||
|
**/
|
||||||
|
lookupCache: function(url) {
|
||||||
|
return this.localCache[url];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Store the contents of a Onebox in our local cache.
|
||||||
|
|
||||||
|
@method cache
|
||||||
|
@private
|
||||||
|
@param {String} url the url of the onebox we crawled
|
||||||
|
@param {String} contents the contents we want to cache
|
||||||
|
**/
|
||||||
|
cache: function(url, contents) {
|
||||||
|
this.localCache[url] = contents;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
};
|
||||||
load: load,
|
|
||||||
lookup: lookup,
|
|
||||||
lookupCache: lookupCache
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -152,19 +152,27 @@ Discourse.ComposerView = Discourse.View.extend({
|
||||||
|
|
||||||
// Called after the preview renders. Debounced for performance
|
// Called after the preview renders. Debounced for performance
|
||||||
afterRender: Discourse.debounce(function() {
|
afterRender: Discourse.debounce(function() {
|
||||||
var $wmdPreview, refresh,
|
var $wmdPreview = $('#wmd-preview');
|
||||||
_this = this;
|
if ($wmdPreview.length === 0) return;
|
||||||
$wmdPreview = $('#wmd-preview');
|
|
||||||
if ($wmdPreview.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Discourse.SyntaxHighlighting.apply($wmdPreview);
|
Discourse.SyntaxHighlighting.apply($wmdPreview);
|
||||||
refresh = this.get('controller.content.post.id') !== void 0;
|
|
||||||
|
var post = this.get('controller.content.post');
|
||||||
|
var refresh = false;
|
||||||
|
|
||||||
|
// If we are editing a post, we'll refresh its contents once. This is a feature that
|
||||||
|
// allows a user to refresh its contents once.
|
||||||
|
if (post && post.blank('refreshedPost')) {
|
||||||
|
refresh = true
|
||||||
|
post.set('refreshedPost', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the post processing effects
|
||||||
$('a.onebox', $wmdPreview).each(function(i, e) {
|
$('a.onebox', $wmdPreview).each(function(i, e) {
|
||||||
return Discourse.Onebox.load(e, refresh);
|
Discourse.Onebox.load(e, refresh);
|
||||||
});
|
});
|
||||||
return $('span.mention', $wmdPreview).each(function(i, e) {
|
$('span.mention', $wmdPreview).each(function(i, e) {
|
||||||
return Discourse.Mention.load(e, refresh);
|
Discourse.Mention.load(e, refresh);
|
||||||
});
|
});
|
||||||
}, 100),
|
}, 100),
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,16 @@ class OneboxController < ApplicationController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
Oneboxer.invalidate(params[:url]) if params[:refresh].present?
|
Oneboxer.invalidate(params[:url]) if params[:refresh].present?
|
||||||
render text: Oneboxer.preview(params[:url])
|
|
||||||
|
result = Oneboxer.preview(params[:url])
|
||||||
|
result.strip! if result.present?
|
||||||
|
|
||||||
|
# If there is no result, return a 404
|
||||||
|
if result.blank?
|
||||||
|
render nothing: true, status: 404
|
||||||
|
else
|
||||||
|
render text: result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,14 +2,46 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe OneboxController do
|
describe OneboxController do
|
||||||
|
|
||||||
it 'asks the oneboxer for the preview' do
|
let(:url) { "http://google.com" }
|
||||||
Oneboxer.expects(:preview).with('http://google.com')
|
|
||||||
xhr :get, :show, url: 'http://google.com'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'invalidates the cache if refresh is passed' do
|
it 'invalidates the cache if refresh is passed' do
|
||||||
Oneboxer.expects(:invalidate).with('http://google.com')
|
Oneboxer.expects(:invalidate).with(url)
|
||||||
xhr :get, :show, url: 'http://google.com', refresh: true
|
xhr :get, :show, url: url, refresh: true
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "found onebox" do
|
||||||
|
|
||||||
|
let(:body) { "this is the onebox body"}
|
||||||
|
|
||||||
|
before do
|
||||||
|
Oneboxer.expects(:preview).with(url).returns(body)
|
||||||
|
xhr :get, :show, url: url
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns success' do
|
||||||
|
response.should be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the onebox response in the body' do
|
||||||
|
response.body.should == body
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "missing onebox" do
|
||||||
|
|
||||||
|
it "returns 404 if the onebox is nil" do
|
||||||
|
Oneboxer.expects(:preview).with(url).returns(nil)
|
||||||
|
xhr :get, :show, url: url
|
||||||
|
response.response_code.should == 404
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 if the onebox is an empty string" do
|
||||||
|
Oneboxer.expects(:preview).with(url).returns(" \t ")
|
||||||
|
xhr :get, :show, url: url
|
||||||
|
response.response_code.should == 404
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,19 +2,22 @@
|
||||||
|
|
||||||
describe("Discourse.Onebox", function() {
|
describe("Discourse.Onebox", function() {
|
||||||
|
|
||||||
|
var anchor;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
spyOn(jQuery, 'ajax').andCallThrough();
|
spyOn(jQuery, 'ajax').andCallThrough();
|
||||||
|
anchor = $("<a href='http://bla.com'></a>")[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Stops rapid calls with cache true", function() {
|
it("Stops rapid calls with cache true", function() {
|
||||||
Discourse.Onebox.lookup('http://bla.com', true, function(c) { return c; });
|
Discourse.Onebox.load(anchor, true);
|
||||||
Discourse.Onebox.lookup('http://bla.com', true, function(c) { return c; });
|
Discourse.Onebox.load(anchor, true);
|
||||||
expect(jQuery.ajax.calls.length).toBe(1);
|
expect(jQuery.ajax.calls.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Stops rapid calls with cache false", function() {
|
it("Stops rapid calls with cache false", function() {
|
||||||
Discourse.Onebox.lookup('http://bla.com/a', false, function(c) { return c; });
|
Discourse.Onebox.load(anchor, false);
|
||||||
Discourse.Onebox.lookup('http://bla.com/a', false, function(c) { return c; });
|
Discourse.Onebox.load(anchor, false);
|
||||||
expect(jQuery.ajax.calls.length).toBe(1);
|
expect(jQuery.ajax.calls.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue