AtlasCamp 2013: Modernizing your Plugin UI
-
Upload
colleenfry -
Category
Technology
-
view
138 -
download
0
description
Transcript of AtlasCamp 2013: Modernizing your Plugin UI
#atlascamp@atlassian
Jonathon Creenaune, Front End Architect, Atlassian
Modernizing YourPlugin UI
Build more features
Build more features
Make features better
Make features better
Beauty
Beauty
Interactivity
Beauty
<header class="aui-page-header">
<div class="aui-page-header-inner">
my header text
</div>
</header>
AUI
<header class="aui-page-header">
<div class="aui-page-header-inner">
my header text
</div>
</header>
Side-by-side
<header class="aui-page-header">
<div class="aui-page-header-inner">
my header text
</div>
</header>
Side-by-side
{call aui.page.pageHeader}
{param content}
my header text
{/param}
{/call}
AUI 5.0 is in
JIRA 6.0Confluence 5.0
Bamboo 4.3Stash 2.0
FE/CRU 3.0
Targeting non-soy platforms
URLs should be designed for the user, not just a side-effect of the technology you used to create a page
“”
/secure/MemeAction.jspa
/plugins/servlet/memes
/memes
/memes/{x}
/memes/create/
/memes/create/{x}
/memes/create
/secure/MemeAction.jspa
/secure/MemeAction.jspa?key={x}
/secure/MemeAction.jspa?upload=
/secure/MemeAction.jspa?createBaseImageKey={x}
/secure/MemeAction.jspa?create=
=>
<routing key="meme-pretty-urls" path="/memes">
<get from="" to="/secure/MemeAction.jspa"/>
<get from="/create/" to="/secure/MemeAction.jspa?upload="/>
<get from="/create/{key}" to="/secure/MemeAction.jspa?createBaseImageKey={key}"/>
<get from="/create" to="/secure/MemeAction.jspa?create="/>
<get from="/{key}" to="/secure/MemeAction.jspa?key={key}"/>
</routing>
Pretty URLs
P2: YesOld products:Yes
Connect:Yes
Pretty URLs
Interactivity
Getting code closer to the user
history.pushState(stateObject, title, url);
// Using history.js
History.pushState(null, null, url);
Pushstate
gallery.onSelectImage(function(key) {
// Add it to the URL
History.pushState(null, null, AJS.contextPath() + “/memes” + key);
// Load the hero image
hero.load($container, key);
});
Pushstate
History.Adapter.bind(window, 'statechange', function() {
var match;
if (match = getUrl().match(/^$/)) { // gallery
gallery.load($container);
}
else if (match = getUrl().match(/^\/(.*)$/)) { // hero
hero.load($container, match[1]);
}
});
Pushstate - back / forward
P2: YesOld products:Yes
Connect:Yes
Pushstate
Soy renders on server
and on client
<webwork1 key="meme-webwork" class="java.lang.Object">
<actions>
<action name="com.atlassian.meme.action.MemeAction" ...>
<view name="hero" type="soy">
:server-templates/meme.page.hero
</view>
<view name="gallery" type="soy">
:server-templates/meme.page.gallery
</view>
...
Render on server
$(function() {
var $container = getContainer();
hero.initEvents($container);
});
Render on server
<web-resource key="hero">
<transformation extension="soy">
<transformer key="soyTransformer" />
</transformation>
<resource type="download" name="hero.js" location="hero.soy"/>
...
Render on client
function render($container, key) {
$.get(getMemeUrl(key)).done(function(memeData) {
$container.html(meme.hero.main({
meme: memeData
}));
initEvents($container);
...
Render on client
P2: YesOld products: If soy available
Connect:JVM server
Render on client + server
// Bad
<script>
var myData = “${getMyData}”;
</script>
// Why? Because it’s an XSS hole
<script>
var myData = “</script><script>alert(‘naughty’);””;
</script>
Injecting Page Data
// In action
public String getSelectData() {
return ImmutableMap.of(
“baseImages”, getAllBaseImages(),
“baseImagesJson”, marshalAsJson(getAllBaseImages())
);
}
Injecting Page Data
// In template
<div class="base-images-json"
data-base-images-json="{$baseImagesJson}">
</div>
Injecting Page Data
// From javascript
var myData = AJS.$(".base-images-json").
data("base-images-json");
Accessing Page Data
P2: YesOld products:Yes
Connect:Yes
Page Data
Future ...
<web-resource key="blah">
<data name="my-data" provider="com.acme.MyDataProvider" />
<resource type="download" name="my-code.js" />
</web-resource>
Injecting Page Data
// pageBuilderService is injectable
pageBuilderService.getData().set("my-data-key", myJsonable);
Injecting Page Data
var data = AJS.data.get("my-plugin:blah:my-data");
Accessing Page Data
Main
Gallery
Hero
Create
Select
Form
Utils
Read files
Render Meme
define(“create/canvasDrawer”, function() {
...
return {
drawImage: function() {},
drawText: function() {},
getAsBase64: function() {}
}
});
Defining Modules
define(“create/main”, [“./formView”, “./canvasDrawer”],
function(formView, canvasDrawer) {
formView.onSubmit(function() {
canvasDrawer.drawImage(myImage);
save(canvasDrawer.getAsBase64);
});
});
Requiring Modules
almond.js
P2: YesOld products:Yes
Connect:Yes
JS modules
Amazon: For every 100ms increase in load time of Amazon.com decreased sales by 1%
Google: From 10 results in 0.4 seconds to to 30 results in 0.9 seconds decreased traffic and ad revenues by 20%
Google: An extra 500ms in loading time resulted in 20% drop in traffic.
Yahoo: 400ms slower page would see 5-9% more people leave before the page finished loading.
“
”
Main
Gallery
Hero
Create
Select
Form
Utils
Read Files
Render Meme
Main
Gallery
Hero
Create
Select
Form
Utils
Read Files
Render Meme
<web-resource key="create">
<resource type="download" name="create/main.js" location="create/main.js" />
<resource type="download" name="create/create.css" location="create/create.css" />
<resource type="download" name="create/create.js" location="create/create.soy" />
...
</web-resource>
Async Resource Loading
require("gallery/main").onSelectCreate(function() { WRM.require([ "wr!com.atlassian.atlassian-meme-generator:create" ]).done(function() { showCreateView(); });});
Async Resource Loading
P2: YesOld products:Yes
Connect:Other libs
Async Resource Loading
Beauty:
SoyPretty URLs
Interactivity:
PushstateServer and Client rendering
DataJS modules
Async resource loading
Sample App:
http://bitbucket.org/jcreenaune/atlassian-meme-
generator
AUI Debugger:
http://bit.ly/10DXYlG
Thank you!
Party Starts at 19.00!SkyLounge, Oosterdoksstraat 4, 11th Floor