From c18222ea51993d594c71ea191fd443d4cf274122 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Tue, 1 Oct 2013 10:22:54 +0200 Subject: [PATCH 01/11] Settings pane This commit contains a settings pane implementation. There are a couple of things here, which are not used yet, like advanced form helpers. I'm leaving them here, because the plan is to add support for more settings soon (like: include/exclude branch patterns), which will need these helpers. There is also tabs support, although in the current version there is only one tab (initially it was created for supporting general tab and notifications tab). --- assets/scripts/app/auth.coffee | 2 +- assets/scripts/app/controllers.coffee | 18 ++ assets/scripts/app/helpers/handlebars.coffee | 286 ++++++++++++++++++ assets/scripts/app/models/repo.coffee | 7 + assets/scripts/app/routes.coffee | 51 +++- .../scripts/app/templates/layouts/profile.hbs | 2 +- assets/scripts/app/templates/profile/repo.hbs | 3 + .../app/templates/profile/repo/settings.hbs | 29 ++ assets/scripts/app/templates/repos/show.hbs | 2 +- assets/scripts/app/views.coffee | 1 + assets/scripts/lib/travis/ajax.coffee | 3 +- .../spec/unit/settings_helpers_spec.coffee | 28 ++ assets/styles/app/loading.sass | 2 +- 13 files changed, 419 insertions(+), 15 deletions(-) create mode 100644 assets/scripts/app/templates/profile/repo.hbs create mode 100644 assets/scripts/app/templates/profile/repo/settings.hbs create mode 100644 assets/scripts/spec/unit/settings_helpers_spec.coffee diff --git a/assets/scripts/app/auth.coffee b/assets/scripts/app/auth.coffee index 565a18c1..b8116df7 100644 --- a/assets/scripts/app/auth.coffee +++ b/assets/scripts/app/auth.coffee @@ -74,7 +74,7 @@ try router.send('afterSignIn') catch e - throw e unless e =~ /There are no active handlers/ + throw e unless e =~ /There are no active handlers/ || e =~ /Can't trigger action "afterSignIn/ @refreshUserData(data.user) refreshUserData: (user) -> diff --git a/assets/scripts/app/controllers.coffee b/assets/scripts/app/controllers.coffee index f1b4f520..bcec80fd 100644 --- a/assets/scripts/app/controllers.coffee +++ b/assets/scripts/app/controllers.coffee @@ -48,6 +48,24 @@ Travis.FirstSyncController = Em.Controller.extend isSyncing: Ember.computed.alias('user.isSyncing') +Travis.ProfileRepoController = Em.ObjectController.extend() +Travis.ProfileRepoSettingsTabController = Em.ObjectController.extend() + +Travis.ProfileRepoSettingsController = Em.Controller.extend + needs: ['profileRepoSettingsTab', 'profileRepo'] + tab: Ember.computed.alias('controllers.profileRepoSettingsTab.model.tab') + repo: Ember.computed.alias('controllers.profileRepo.content') + + submit: -> + @set('saving', true) + self = this + @get('repo').saveSettings(@get('settings')).then -> + self.set('saving', false) + Travis.flash(success: 'Settings were saved successfully') + , -> + self.set('saving', false) + Travis.flash(error: 'There was an error while saving settings. Please try again.') + require 'controllers/accounts' require 'controllers/build' require 'controllers/builds' diff --git a/assets/scripts/app/helpers/handlebars.coffee b/assets/scripts/app/helpers/handlebars.coffee index 8dfda033..63e6cc15 100644 --- a/assets/scripts/app/helpers/handlebars.coffee +++ b/assets/scripts/app/helpers/handlebars.coffee @@ -3,6 +3,292 @@ require 'ext/ember/bound_helper' safe = (string) -> new Handlebars.SafeString(string) +Travis.Tab = Ember.Object.extend + show: -> + @get('tabs').forEach( (t) -> t.hide() ) + @set('visible', true) + + hide: -> + @set('visible', false) + +Travis.TabsView = Ember.View.extend + tabBinding: 'controller.tab' + tabsBinding: 'controller.tabs' + + tabDidChange: (-> + @activateTab(@get('tab')) + ).observes('tab') + + tabsDidChange: (-> + tab = @get('tab') + if tab + @activateTab(tab) + else if @get('tabs.length') + @activateTab(@get('tabs.firstObject.id')) + ).observes('tabs.length', 'tabs') + + activateTab: (tabId) -> + tab = @get('tabs').findBy('id', tabId) + + return unless tab + + tab.show() unless tab.get('visible') + + layout: Ember.Handlebars.compile( + '<ul class="tabs">' + + ' {{#each tab in tabs}}' + + ' <li {{bindAttr class="tab.visible:active"}}>' + + ' <h5>{{#linkTo "profile.repo.settings.tab" tab.id}}{{tab.name}}{{/linkTo}}</h5>' + + ' </li>' + + ' {{/each}}' + + '</ul>' + + '{{yield}}') + +Travis.TabView = Ember.View.extend + attributeBindings: ['style'] + + style: (-> + if !@get('tab.visible') + 'display: none' + ).property('tab.visible') + +Ember.Handlebars.registerHelper('travis-tab', (id, name, options) -> + controller = this + controller.set('tabs', []) unless controller.get('tabs') + + tab = Travis.Tab.create(id: id, name: name, tabs: controller.get('tabs')) + + view = Travis.TabView.create( + controller: this + tab: tab + ) + + controller = this + Ember.run.schedule('afterRender', -> + if controller.get('tabs.length') == 0 + tab.show() + controller.get('tabs').pushObject(tab) + ) + + Ember.Handlebars.helpers.view.call(this, view, options) +) + + +Ember.Handlebars.registerHelper('travis-tabs', (options) -> + template = options.fn + delete options.fn + + @set('tabs', []) + + view = Travis.TabsView.create( + controller: this + template: template + ) + + Ember.Handlebars.helpers.view.call(this, view, options) +) + +Travis.SettingsMultiplierView = Ember.CollectionView.extend() + +createObjects = (path, offset) -> + segments = path.split('.') + if segments.length > offset + for i in [1..(segments.length - offset)] + path = segments.slice(0, i).join('.') + if Ember.isNone(Ember.get(this, path)) + Ember.set(this, path, {}) + + return segments + +Ember.Handlebars.registerHelper('settings-multiplier', (path, options) -> + template = options.fn + delete options.fn + + parentsPath = getSettingsPath(options.data.view) + if parentsPath && parentsPath != '' + path = parentsPath + '.' + path + + createObjects.call(this, path, 1) + + if Ember.isNone(@get(path)) + collection = [{}] + @set(path, collection) + + + itemViewClass = Ember.View.extend( + template: template, + controller: this, + tagName: 'li', + multiplier: true + ) + + view = Travis.SettingsMultiplierView.create( + contentBinding: 'controller.' + path + controller: this + tagName: 'ul' + itemViewClass: itemViewClass + fields: [] + settingsPath: path + ) + + view.addObserver('content.length', -> + if @get('content.length') == 0 + @get('content').pushObject({}) + ) + + Ember.Handlebars.helpers.view.call(this, view, options) +) + +Travis.FormSettingsView = Ember.View.extend Ember.TargetActionSupport, + target: Ember.computed.alias('controller') + actionContext: Ember.computed.alias('context'), + action: 'submit' + tagName: 'form' + submit: (event) -> + event.preventDefault() + @triggerAction() + + +Ember.Handlebars.registerHelper('settings-form', (path, options) -> + if arguments.length == 1 + options = path + path = 'settings' + + view = Travis.FormSettingsView.create( + template: options.fn + controller: this + settingsPath: path + ) + + delete options.fn + + Ember.Handlebars.helpers.view.call(this, view, options) +) + +Ember.Handlebars.helper('settings-select', (options) -> + view = options.data.view + optionValues = options.hash.options + delete options.hash.options + + originalPath = options.hash.value + + parentsPath = getSettingsPath(view) + #TODO: such checks should also check parents, not only current context view + if !view.get('multiplier') && parentsPath && parentsPath != '' + originalPath = parentsPath + '.' + originalPath + + fullPath = originalPath + + if view.get('multiplier') + fullPath = 'view.content.' + fullPath + + createObjects.call(this, fullPath, 1) + + # TODO: setting a value here does not work and we still need + # a valueBinding in the view, I'm not sure why + options.hash.value = fullPath + + selectView = Ember.Select.create( + content: [''].pushObjects(optionValues.split(',')) + controller: this + valueBinding: 'controller.' + fullPath + ) + + Ember.Handlebars.helpers.view.call(this, selectView, options) +) + + + +Ember.Handlebars.helper('settings-remove-link', (options) -> + view = Ember.View.extend( + tagName: 'a' + attributeBindings: ['href', 'style'] + href: '#' + style: (-> + # TODO: do not assume that we're direct child + if @get('parentView.parentView.content.length') == 1 + 'display: none' + ).property('parentView.parentView.content.length') + template: Ember.Handlebars.compile('remove') + controller: this + click: (event) -> + event.preventDefault() + + if content = @get('parentView.content') + @get('parentView.parentView.content').removeObject(content) + ).create() + + Ember.Handlebars.helpers.view.call(this, view, options) +) + +Ember.Handlebars.registerHelper('settings-add-link', (path, options) -> + parentsPath = getSettingsPath(options.data.view) + if parentsPath && parentsPath != '' + path = parentsPath + '.' + path + + view = Ember.View.create( + tagName: 'a' + attributeBindings: ['href'] + href: '#' + template: options.fn + controller: this + click: (event) -> + event.preventDefault() + + if collection = @get('controller.' + path) + collection.pushObject({}) + ) + + Ember.Handlebars.helpers.view.call(this, view, options) +) + +getSettingsPath = (view) -> + settingsPaths = [] + if settingsPath = view.get('settingsPath') + settingsPaths.pushObject settingsPath + + parent = view + while parent = parent.get('parentView') + if settingsPath = parent.get('settingsPath') + settingsPaths.pushObject settingsPath + + return settingsPaths.reverse().join('.') + +Ember.Handlebars.helper('settings-input', (options) -> + view = options.data.view + + if options.hash.type == 'checkbox' + originalPath = options.hash.checked + else + originalPath = options.hash.value + + parentsPath = getSettingsPath(view) + #TODO: such checks should also check parents, not only current context view + if !view.get('multiplier') && parentsPath && parentsPath != '' + originalPath = parentsPath + '.' + originalPath + + fullPath = originalPath + + if view.get('multiplier') + fullPath = 'view.content.' + fullPath + + if options.hash.type != 'password' + createObjects.call(this, fullPath, 1) + else + createObjects.call(view, fullPath, 2) + content = view.get('content') + fullPath += ".value" + if Ember.isNone(Ember.get(content, originalPath)) + Ember.set(content, originalPath, {}) + Ember.set(content, originalPath + ".type", 'password') + + if options.hash.type == 'checkbox' + options.hash.checked = fullPath + else + options.hash.value = fullPath + Ember.Handlebars.helpers.input.call(this, options) +) + Handlebars.registerHelper 'tipsy', (text, tip) -> safe '<span class="tool-tip" original-title="' + tip + '">' + text + '</span>' diff --git a/assets/scripts/app/models/repo.coffee b/assets/scripts/app/models/repo.coffee index f582fb10..ca098348 100644 --- a/assets/scripts/app/models/repo.coffee +++ b/assets/scripts/app/models/repo.coffee @@ -105,6 +105,13 @@ require 'travis/model' regenerateKey: (options) -> Travis.ajax.ajax '/repos/' + @get('id') + '/key', 'post', options + fetchSettings: -> + Travis.ajax.ajax('/repos/' + @get('id') + '/settings', 'get', forceAuth: true).then (data) -> + data['settings'] + + saveSettings: (settings) -> + Travis.ajax.ajax('/repos/' + @get('id') + '/settings', 'patch', data: { settings: settings }) + @Travis.Repo.reopenClass recent: -> @find() diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index 0f8186c1..ba390fb9 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -27,10 +27,10 @@ Ember.Route.reopen authController.set('redirected', true) @transitionTo('auth') else - throw(error) + @_super(error) renderNoOwnedRepos: -> - @render('no_owned_repos', outlet: 'main') + @render('no_owned_repos') renderFirstSync: -> @renderFirstSync() @@ -41,6 +41,7 @@ Ember.Route.reopen afterSignOut: -> @afterSignOut() + afterSignIn: -> if transition = Travis.auth.get('afterSignInTransition') Travis.auth.set('afterSignInTransition', null) @@ -86,6 +87,7 @@ Travis.Router.reopen @_super.apply this, arguments + Travis.Router.map -> @resource 'index', path: '/', -> @route 'current', path: '/' @@ -103,6 +105,10 @@ Travis.Router.map -> @route 'auth', path: '/auth' @route 'notFound', path: '/not-found' + @resource 'profile.repo', path: '/profile/:owner/:name', -> + @resource 'profile.repo.settings', path: 'settings', -> + @route 'tab', path: ':tab' + @resource 'profile', path: '/profile', -> @route 'index', path: '/' @resource 'account', path: '/:login', -> @@ -125,7 +131,7 @@ Travis.SetupLastBuild = Ember.Mixin.create repo = @controllerFor('repo').get('repo') if repo && repo.get('isLoaded') && !repo.get('lastBuildId') Ember.run.next => - @render('builds/not_found', outlet: 'pane', into: 'repo') + @render('builds/not_found', into: 'repo') Travis.GettingStartedRoute = Ember.Route.extend setupController: -> @@ -158,7 +164,7 @@ Travis.FirstSyncRoute = Ember.Route.extend Travis.IndexCurrentRoute = Ember.Route.extend Travis.SetupLastBuild, renderTemplate: -> @render 'repo' - @render 'build', outlet: 'pane', into: 'repo' + @render 'build', into: 'repo' setupController: -> @_super.apply this, arguments @@ -175,7 +181,7 @@ Travis.IndexCurrentRoute = Ember.Route.extend Travis.SetupLastBuild, Travis.AbstractBuildsRoute = Ember.Route.extend renderTemplate: -> - @render 'builds', outlet: 'pane', into: 'repo' + @render 'builds', into: 'repo' setupController: -> @controllerFor('repo').activate(@get('contentType')) @@ -200,7 +206,7 @@ Travis.BranchesRoute = Travis.AbstractBuildsRoute.extend(contentType: 'branches' Travis.BuildRoute = Ember.Route.extend renderTemplate: -> - @render 'build', outlet: 'pane', into: 'repo' + @render 'build', into: 'repo' serialize: (model, params) -> id = if model.get then model.get('id') else model @@ -221,7 +227,7 @@ Travis.BuildRoute = Ember.Route.extend Travis.JobRoute = Ember.Route.extend renderTemplate: -> - @render 'job', outlet: 'pane', into: 'repo' + @render 'job', into: 'repo' serialize: (model, params) -> id = if model.get then model.get('id') else model @@ -248,7 +254,7 @@ Travis.RepoIndexRoute = Ember.Route.extend Travis.SetupLastBuild, @controllerFor('repo').activate('current') renderTemplate: -> - @render 'build', outlet: 'pane', into: 'repo' + @render 'build', into: 'repo' Travis.RepoRoute = Ember.Route.extend renderTemplate: -> @@ -267,7 +273,6 @@ Travis.RepoRoute = Ember.Route.extend model: (params) -> slug = "#{params.owner}/#{params.name}" - Travis.Repo.fetchBySlug(slug) actions: @@ -324,7 +329,7 @@ Travis.ProfileRoute = Ember.Route.extend @render 'top', outlet: 'top' @render 'accounts', outlet: 'left' @render 'flash', outlet: 'flash' - @render 'profile' + @_super.apply(this, arguments) Travis.ProfileIndexRoute = Ember.Route.extend setupController: -> @@ -392,3 +397,29 @@ Travis.AuthRoute = Ember.Route.extend deactivate: -> @controllerFor('auth').set('redirected', false) + +Travis.ProfileRepoRoute = Travis.ProfileRoute.extend + setupController: (controller, model) -> + # TODO: if repo is just a data hash with id and slug load it + # as incomplete record + model = Travis.Repo.find(model.id) if model && !model.get + @_super(controller, model) + + controller.set('content', model) + + serialize: (repo) -> + slug = if repo.get then repo.get('slug') else repo.slug + [owner, name] = slug.split('/') + { owner: owner, name: name } + + model: (params) -> + slug = "#{params.owner}/#{params.name}" + Travis.Repo.fetchBySlug(slug) + +Travis.ProfileRepoSettingsRoute = Ember.Route.extend + setupController: (controller, model) -> + controller.set('settings', model) + + model: -> + repo = @modelFor('profileRepo') + repo.fetchSettings() diff --git a/assets/scripts/app/templates/layouts/profile.hbs b/assets/scripts/app/templates/layouts/profile.hbs index 6ce4e370..e149cc94 100644 --- a/assets/scripts/app/templates/layouts/profile.hbs +++ b/assets/scripts/app/templates/layouts/profile.hbs @@ -8,7 +8,7 @@ <div id="main"> {{outlet flash}} - {{outlet main}} + {{outlet}} </div> <div id="right"> diff --git a/assets/scripts/app/templates/profile/repo.hbs b/assets/scripts/app/templates/profile/repo.hbs new file mode 100644 index 00000000..43d6cf6c --- /dev/null +++ b/assets/scripts/app/templates/profile/repo.hbs @@ -0,0 +1,3 @@ +<h1>Settings: {{slug}}</h1> + +{{outlet}} diff --git a/assets/scripts/app/templates/profile/repo/settings.hbs b/assets/scripts/app/templates/profile/repo/settings.hbs new file mode 100644 index 00000000..c16eee7e --- /dev/null +++ b/assets/scripts/app/templates/profile/repo/settings.hbs @@ -0,0 +1,29 @@ +{{#travis-tabs}} + {{#travis-tab "general" "General Settings"}} + {{#settings-form}} + <p> + {{settings-input checked=builds_only_with_travis_yml type="checkbox" id="builds-only-with-travis-yml"}} + <label for="builds-only-with-travis-yml">Build only commits with .travis.yml file</label> + </p> + + <p> + {{settings-input checked=build_pushes type="checkbox" id="build-pushes"}} + <label for="build-pushes">Build pushes</label> + </p> + + <p> + {{settings-input checked=build_pull_requests type="checkbox" id="build-pull-requests"}} + <label for="build-pull-requests">Build pull requests</label> + </p> + + <p> + {{#if saving}} + <span class="saving">Saving</span> + {{else}} + <button type="submit">Submit</button> + {{/if}} + </p> + {{/settings-form}} + {{/travis-tab}} +{{/travis-tabs}} + diff --git a/assets/scripts/app/templates/repos/show.hbs b/assets/scripts/app/templates/repos/show.hbs index 7d7d3cf7..0c35e126 100644 --- a/assets/scripts/app/templates/repos/show.hbs +++ b/assets/scripts/app/templates/repos/show.hbs @@ -17,7 +17,7 @@ {{/with}} <div class="tab"> - {{outlet pane}} + {{outlet}} </div> {{else}} <div class="loading"><span>Loading</span></div> diff --git a/assets/scripts/app/views.coffee b/assets/scripts/app/views.coffee index 7ddf2bc8..e9017b1c 100644 --- a/assets/scripts/app/views.coffee +++ b/assets/scripts/app/views.coffee @@ -45,6 +45,7 @@ Travis.FirstSyncView = Travis.View.extend ) , Travis.config.syncingPageRedirectionTime + require 'views/accounts' require 'views/application' require 'views/build' diff --git a/assets/scripts/lib/travis/ajax.coffee b/assets/scripts/lib/travis/ajax.coffee index 00c32f01..fcd82eed 100644 --- a/assets/scripts/lib/travis/ajax.coffee +++ b/assets/scripts/lib/travis/ajax.coffee @@ -23,13 +23,14 @@ Travis.ajax = Em.Object.create !result ajax: (url, method, options) -> + method = method || "GET" method = method.toUpperCase() endpoint = Travis.config.api_endpoint || '' options = options || {} token = Travis.sessionStorage.getItem('travis.token') - if token && Travis.ajax.needsAuth(method, url) + if token && (Travis.ajax.needsAuth(method, url) || options.forceAuth) options.headers ||= {} options.headers['Authorization'] ||= "token #{token}" diff --git a/assets/scripts/spec/unit/settings_helpers_spec.coffee b/assets/scripts/spec/unit/settings_helpers_spec.coffee new file mode 100644 index 00000000..12616e75 --- /dev/null +++ b/assets/scripts/spec/unit/settings_helpers_spec.coffee @@ -0,0 +1,28 @@ +view = null + +module "Handlebars helpers - settings-input", + setup: -> + Ember.run -> Travis.advanceReadiness() + +test "settings input allows to bind to nested objects", -> + controller = Ember.Object.create() + view = Ember.View.create( + controller: controller + template: Ember.Handlebars.compile("{{input value=foo}} {{controller}}") + ) + + Ember.run -> + view.append() + + + Ember.run -> + controller.set('foo', 'bar') + + Ember.run.sync() + + input = view.$('input') + input.val('a value').change() + Ember.run.sync() + + console.log controller.get('foo') + diff --git a/assets/styles/app/loading.sass b/assets/styles/app/loading.sass index 8278162e..2f2a2584 100644 --- a/assets/styles/app/loading.sass +++ b/assets/styles/app/loading.sass @@ -11,7 +11,7 @@ .loading display: none -span.loading +span.loading, span.saving padding: 0 25px 0 0 font-size: $font-size-small color: $color-text-lighter From ac6b394ec4c5e18c5855dc8e5d58d38a5a6afd1b Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 11 Dec 2013 12:39:52 +0100 Subject: [PATCH 02/11] Display settings link in the cog menu --- assets/scripts/app/templates/repos/show/tools.hbs | 6 ++++++ assets/scripts/app/views/repo/show.coffee | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/assets/scripts/app/templates/repos/show/tools.hbs b/assets/scripts/app/templates/repos/show/tools.hbs index b2541b3c..f858248e 100644 --- a/assets/scripts/app/templates/repos/show/tools.hbs +++ b/assets/scripts/app/templates/repos/show/tools.hbs @@ -11,6 +11,12 @@ </a> </li> {{/if}} + {{#if view.displaySettingsLink}} + <li> + {{#linkTo "profile.repo.settings" view.repo}}Settings{{/linkTo}} + </li> + {{/if}} + </ul> {{#if view.displayStatusImages}} <a href="#" id="status-image-popup" name="status-images" class="open-popup" {{action "statusImages" target="view"}}> diff --git a/assets/scripts/app/views/repo/show.coffee b/assets/scripts/app/views/repo/show.coffee index a27a0b29..1d008c24 100644 --- a/assets/scripts/app/views/repo/show.coffee +++ b/assets/scripts/app/views/repo/show.coffee @@ -115,6 +115,11 @@ Travis.reopen permissions.contains parseInt(@get('repo.id')) ).property('currentUser.permissions.length', 'repo.id') + hasPushPermission: (-> + if permissions = @get('currentUser.pushPermissions') + permissions.contains parseInt(@get('repo.id')) + ).property('currentUser.pushPermissions.length', 'repo.id') + hasAdminPermission: (-> if permissions = @get('currentUser.adminPermissions') permissions.contains parseInt(@get('repo.id')) @@ -124,6 +129,10 @@ Travis.reopen Travis.Urls.statusImage(@get('slug')) ).property('slug') + displaySettingsLink: (-> + @get('hasPushPermission') + ).property('hasPushPermission') + displayStatusImages: (-> @get('hasPermission') ).property('hasPermission') From 8482c4c2863f3bfbfe2cc28d83f315814a67c1f7 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 11 Dec 2013 13:34:06 +0100 Subject: [PATCH 03/11] Move settings out of the profile page --- assets/scripts/app/controllers.coffee | 14 +++++------ assets/scripts/app/helpers/handlebars.coffee | 3 ++- assets/scripts/app/routes.coffee | 25 ++++++++----------- .../templates/{profile => }/repo/settings.hbs | 2 ++ assets/scripts/app/templates/repos/show.hbs | 1 - .../app/templates/repos/show/tools.hbs | 2 +- 6 files changed, 22 insertions(+), 25 deletions(-) rename assets/scripts/app/templates/{profile => }/repo/settings.hbs (96%) diff --git a/assets/scripts/app/controllers.coffee b/assets/scripts/app/controllers.coffee index bcec80fd..5a267beb 100644 --- a/assets/scripts/app/controllers.coffee +++ b/assets/scripts/app/controllers.coffee @@ -48,18 +48,16 @@ Travis.FirstSyncController = Em.Controller.extend isSyncing: Ember.computed.alias('user.isSyncing') -Travis.ProfileRepoController = Em.ObjectController.extend() -Travis.ProfileRepoSettingsTabController = Em.ObjectController.extend() - -Travis.ProfileRepoSettingsController = Em.Controller.extend - needs: ['profileRepoSettingsTab', 'profileRepo'] - tab: Ember.computed.alias('controllers.profileRepoSettingsTab.model.tab') - repo: Ember.computed.alias('controllers.profileRepo.content') +Travis.RepoSettingsTabController = Em.ObjectController.extend() +Travis.RepoSettingsController = Em.ObjectController.extend + needs: ['repoSettingsTab'] + tab: Ember.computed.alias('controllers.repoSettingsTab.model.tab') + settings: Ember.computed.alias('model.settings') submit: -> @set('saving', true) self = this - @get('repo').saveSettings(@get('settings')).then -> + @get('model').saveSettings(@get('settings')).then -> self.set('saving', false) Travis.flash(success: 'Settings were saved successfully') , -> diff --git a/assets/scripts/app/helpers/handlebars.coffee b/assets/scripts/app/helpers/handlebars.coffee index 63e6cc15..1074da90 100644 --- a/assets/scripts/app/helpers/handlebars.coffee +++ b/assets/scripts/app/helpers/handlebars.coffee @@ -34,11 +34,12 @@ Travis.TabsView = Ember.View.extend tab.show() unless tab.get('visible') + # TODO: remove hardcoded link layout: Ember.Handlebars.compile( '<ul class="tabs">' + ' {{#each tab in tabs}}' + ' <li {{bindAttr class="tab.visible:active"}}>' + - ' <h5>{{#linkTo "profile.repo.settings.tab" tab.id}}{{tab.name}}{{/linkTo}}</h5>' + + ' <h5>{{#linkTo "repo.settings.tab" tab.id}}{{tab.name}}{{/linkTo}}</h5>' + ' </li>' + ' {{/each}}' + '</ul>' + diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index ba390fb9..fedc4127 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -99,16 +99,17 @@ Travis.Router.map -> @resource 'pullRequests', path: '/pull_requests' @resource 'branches', path: '/branches' + # this can't be nested in repo, because we want a set of different + # templates rendered for settings (for example no "current", "builds", ... tabs) + @resource 'repo.settings', path: '/:owner/:name/settings', -> + @route 'tab', path: ':tab' + @route 'getting_started' @route 'first_sync' @route 'stats', path: '/stats' @route 'auth', path: '/auth' @route 'notFound', path: '/not-found' - @resource 'profile.repo', path: '/profile/:owner/:name', -> - @resource 'profile.repo.settings', path: 'settings', -> - @route 'tab', path: ':tab' - @resource 'profile', path: '/profile', -> @route 'index', path: '/' @resource 'account', path: '/:login', -> @@ -398,15 +399,13 @@ Travis.AuthRoute = Ember.Route.extend deactivate: -> @controllerFor('auth').set('redirected', false) -Travis.ProfileRepoRoute = Travis.ProfileRoute.extend +Travis.RepoSettingsRoute = Ember.Route.extend setupController: (controller, model) -> # TODO: if repo is just a data hash with id and slug load it # as incomplete record model = Travis.Repo.find(model.id) if model && !model.get @_super(controller, model) - controller.set('content', model) - serialize: (repo) -> slug = if repo.get then repo.get('slug') else repo.slug [owner, name] = slug.split('/') @@ -416,10 +415,8 @@ Travis.ProfileRepoRoute = Travis.ProfileRoute.extend slug = "#{params.owner}/#{params.name}" Travis.Repo.fetchBySlug(slug) -Travis.ProfileRepoSettingsRoute = Ember.Route.extend - setupController: (controller, model) -> - controller.set('settings', model) - - model: -> - repo = @modelFor('profileRepo') - repo.fetchSettings() + afterModel: (repo) -> + # I'm using afterModel to fetch settings, because model is not always called. + # If link-to already provides a model, it will be just set as a route context. + repo.fetchSettings().then (settings) -> + repo.set('settings', settings) diff --git a/assets/scripts/app/templates/profile/repo/settings.hbs b/assets/scripts/app/templates/repo/settings.hbs similarity index 96% rename from assets/scripts/app/templates/profile/repo/settings.hbs rename to assets/scripts/app/templates/repo/settings.hbs index c16eee7e..fcab8474 100644 --- a/assets/scripts/app/templates/profile/repo/settings.hbs +++ b/assets/scripts/app/templates/repo/settings.hbs @@ -1,3 +1,5 @@ +<h1>Settings: {{slug}}</h1> + {{#travis-tabs}} {{#travis-tab "general" "General Settings"}} {{#settings-form}} diff --git a/assets/scripts/app/templates/repos/show.hbs b/assets/scripts/app/templates/repos/show.hbs index 0c35e126..07e62096 100644 --- a/assets/scripts/app/templates/repos/show.hbs +++ b/assets/scripts/app/templates/repos/show.hbs @@ -24,4 +24,3 @@ {{/if}} {{/if}} </div> - diff --git a/assets/scripts/app/templates/repos/show/tools.hbs b/assets/scripts/app/templates/repos/show/tools.hbs index f858248e..1dba6858 100644 --- a/assets/scripts/app/templates/repos/show/tools.hbs +++ b/assets/scripts/app/templates/repos/show/tools.hbs @@ -13,7 +13,7 @@ {{/if}} {{#if view.displaySettingsLink}} <li> - {{#linkTo "profile.repo.settings" view.repo}}Settings{{/linkTo}} + {{#linkTo "repo.settings" view.repo}}Settings{{/linkTo}} </li> {{/if}} From 3a15b037da9daaa77c95fe18f7228cf8f9037855 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Thu, 12 Dec 2013 14:55:01 +0100 Subject: [PATCH 04/11] Link to repo settings from a hooks page --- assets/scripts/app/models/hook.coffee | 10 ++++++++++ assets/scripts/app/templates/profile/tabs/hooks.hbs | 2 +- assets/styles/profile/hooks.sass | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/assets/scripts/app/models/hook.coffee b/assets/scripts/app/models/hook.coffee index f35166ac..58fff636 100644 --- a/assets/scripts/app/models/hook.coffee +++ b/assets/scripts/app/models/hook.coffee @@ -27,3 +27,13 @@ require 'travis/model' return if @get('isSaving') @set 'active', !@get('active') @save() + + repo: (-> + # I don't want to make an ajax request for each repository showed in profile, + # especially, because most of them does not have any builds anyway. That's why + # I add an info which we have here to the store - this will allow to display + # a link to the repo and if more info is needed, it will be requested when the + # link is used + Travis.loadOrMerge(Travis.Repo, @getProperties('id', 'slug', 'name', 'ownerName'), skipIfExists: true) + Travis.Repo.find(@get('id')) + ).property('id') diff --git a/assets/scripts/app/templates/profile/tabs/hooks.hbs b/assets/scripts/app/templates/profile/tabs/hooks.hbs index dc920183..0cae8362 100644 --- a/assets/scripts/app/templates/profile/tabs/hooks.hbs +++ b/assets/scripts/app/templates/profile/tabs/hooks.hbs @@ -23,7 +23,7 @@ <p class="description">{{hook.description}}</p> <div class="controls"> - <a {{bindAttr href="hook.urlGithubAdmin"}} class="github-admin tool-tip" title="Github service hooks admin page"></a> + {{#link-to "repo.settings" hook.repo class="repo-settings-icon tool-tip" title="Repository settings"}}{{/link-to}} <a {{action "toggle" hook}} class="switch"> {{#if hook.active}} ON diff --git a/assets/styles/profile/hooks.sass b/assets/styles/profile/hooks.sass index 5566b966..6c003d4a 100644 --- a/assets/styles/profile/hooks.sass +++ b/assets/styles/profile/hooks.sass @@ -57,14 +57,14 @@ float: left display: block - .github-admin + .repo-settings-icon // Overriding an earlier definition above, which is probably // obsolete. TODO: Remove if so. position: relative height: 20px width: 20px padding-right: 0 - background: inline-image('ui/github-admin.png') no-repeat 3px 4px + background: inline-image('ui/repo-settings.png') no-repeat 3px 4px .switch position: relative From c8575b3f636f3abea1649ba42efaaaa9e2230595 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Thu, 12 Dec 2013 14:55:43 +0100 Subject: [PATCH 05/11] Forgot to add repo-settings.png --- assets/images/ui/repo-settings.png | Bin 0 -> 231 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/images/ui/repo-settings.png diff --git a/assets/images/ui/repo-settings.png b/assets/images/ui/repo-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..96fe3fa5e0cfe6dd32fab112f8922132d865bc48 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!L4Z$)>#}9bwr}6QWy_YGJ9pl? zb?eflOLy+vdGO%D@#Dw$@8AFI*|S%#UY$F4?*IS)D^{$ycI{fl1;Ix^y?iA>e!)ON z1YmGIaOVV2Cc@LjF+}2W?fKJu4F()87jH8y-L~le|LyO783ayKPm$=j{()I*Ut`j5 zw?*zhm}cJ6@i$;N#lSr2#R-ds;yHU&neD1H4m{l)u`SHgJGlRav`foE=^azv=gaR9 W;977mIp#Ic4hBzGKbLh*2~7Z_VQ<<1 literal 0 HcmV?d00001 From 8aafb8d4e6c1a9be8c1f6d781ef19de63dd2198e Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Thu, 12 Dec 2013 15:24:30 +0100 Subject: [PATCH 06/11] Bring back 'outlet pane' and 'outlet pane' For some reason (I haven't had time to debug it) when we don't use named outlet rendering "into" does not work in certain circumstances (for example in index current view, where repos are changed automatically). --- assets/scripts/app/routes.coffee | 14 +++++++------- assets/scripts/app/templates/layouts/profile.hbs | 2 +- assets/scripts/app/templates/repos/show.hbs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index fedc4127..06a20676 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -30,7 +30,7 @@ Ember.Route.reopen @_super(error) renderNoOwnedRepos: -> - @render('no_owned_repos') + @render('no_owned_repos', outlet: 'main') renderFirstSync: -> @renderFirstSync() @@ -132,7 +132,7 @@ Travis.SetupLastBuild = Ember.Mixin.create repo = @controllerFor('repo').get('repo') if repo && repo.get('isLoaded') && !repo.get('lastBuildId') Ember.run.next => - @render('builds/not_found', into: 'repo') + @render('builds/not_found', into: 'repo', outlet: 'pane') Travis.GettingStartedRoute = Ember.Route.extend setupController: -> @@ -165,7 +165,7 @@ Travis.FirstSyncRoute = Ember.Route.extend Travis.IndexCurrentRoute = Ember.Route.extend Travis.SetupLastBuild, renderTemplate: -> @render 'repo' - @render 'build', into: 'repo' + @render 'build', into: 'repo', outlet: 'pane' setupController: -> @_super.apply this, arguments @@ -182,7 +182,7 @@ Travis.IndexCurrentRoute = Ember.Route.extend Travis.SetupLastBuild, Travis.AbstractBuildsRoute = Ember.Route.extend renderTemplate: -> - @render 'builds', into: 'repo' + @render 'builds', into: 'repo', outlet: 'pane' setupController: -> @controllerFor('repo').activate(@get('contentType')) @@ -207,7 +207,7 @@ Travis.BranchesRoute = Travis.AbstractBuildsRoute.extend(contentType: 'branches' Travis.BuildRoute = Ember.Route.extend renderTemplate: -> - @render 'build', into: 'repo' + @render 'build', into: 'repo', outlet: 'pane' serialize: (model, params) -> id = if model.get then model.get('id') else model @@ -228,7 +228,7 @@ Travis.BuildRoute = Ember.Route.extend Travis.JobRoute = Ember.Route.extend renderTemplate: -> - @render 'job', into: 'repo' + @render 'job', into: 'repo', outlet: 'pane' serialize: (model, params) -> id = if model.get then model.get('id') else model @@ -255,7 +255,7 @@ Travis.RepoIndexRoute = Ember.Route.extend Travis.SetupLastBuild, @controllerFor('repo').activate('current') renderTemplate: -> - @render 'build', into: 'repo' + @render 'build', into: 'repo', outlet: 'pane' Travis.RepoRoute = Ember.Route.extend renderTemplate: -> diff --git a/assets/scripts/app/templates/layouts/profile.hbs b/assets/scripts/app/templates/layouts/profile.hbs index e149cc94..6ce4e370 100644 --- a/assets/scripts/app/templates/layouts/profile.hbs +++ b/assets/scripts/app/templates/layouts/profile.hbs @@ -8,7 +8,7 @@ <div id="main"> {{outlet flash}} - {{outlet}} + {{outlet main}} </div> <div id="right"> diff --git a/assets/scripts/app/templates/repos/show.hbs b/assets/scripts/app/templates/repos/show.hbs index 07e62096..3b804bba 100644 --- a/assets/scripts/app/templates/repos/show.hbs +++ b/assets/scripts/app/templates/repos/show.hbs @@ -17,7 +17,7 @@ {{/with}} <div class="tab"> - {{outlet}} + {{outlet pane}} </div> {{else}} <div class="loading"><span>Loading</span></div> From c48f70a38bc995bbeca1fc0f60d62e0b450e9364 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 18 Dec 2013 01:55:13 +0100 Subject: [PATCH 07/11] Finish test for settings-input --- .../spec/unit/settings_helpers_spec.coffee | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/assets/scripts/spec/unit/settings_helpers_spec.coffee b/assets/scripts/spec/unit/settings_helpers_spec.coffee index 12616e75..bea181a1 100644 --- a/assets/scripts/spec/unit/settings_helpers_spec.coffee +++ b/assets/scripts/spec/unit/settings_helpers_spec.coffee @@ -8,21 +8,21 @@ test "settings input allows to bind to nested objects", -> controller = Ember.Object.create() view = Ember.View.create( controller: controller - template: Ember.Handlebars.compile("{{input value=foo}} {{controller}}") + template: Ember.Handlebars.compile("{{settings-input value=foo.bar.baz}}") ) Ember.run -> - view.append() + view.appendTo($("#ember-testing")[0]) - Ember.run -> - controller.set('foo', 'bar') - - Ember.run.sync() - input = view.$('input') - input.val('a value').change() - Ember.run.sync() - console.log controller.get('foo') + Ember.run -> + input.val('a value').change() + equal(controller.get('foo.bar.baz'), 'a value') + + Ember.run -> + controller.set('foo.bar.baz', 'a new value') + + equal(input.val(), 'a new value') From cc026b75a8d483878a2241943b15b2c279606d13 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 18 Dec 2013 11:19:06 +0100 Subject: [PATCH 08/11] Make settings header link to repo page --- .../scripts/app/templates/repo/settings.hbs | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/assets/scripts/app/templates/repo/settings.hbs b/assets/scripts/app/templates/repo/settings.hbs index fcab8474..e721c254 100644 --- a/assets/scripts/app/templates/repo/settings.hbs +++ b/assets/scripts/app/templates/repo/settings.hbs @@ -1,31 +1,32 @@ -<h1>Settings: {{slug}}</h1> +<div id="repo"> + <h3>Settings: {{#linkTo "repo" this}}{{slug}}{{/linkTo}}</h3> -{{#travis-tabs}} - {{#travis-tab "general" "General Settings"}} - {{#settings-form}} - <p> - {{settings-input checked=builds_only_with_travis_yml type="checkbox" id="builds-only-with-travis-yml"}} - <label for="builds-only-with-travis-yml">Build only commits with .travis.yml file</label> - </p> + {{#travis-tabs}} + {{#travis-tab "general" "General Settings"}} + {{#settings-form}} + <p> + {{settings-input checked=builds_only_with_travis_yml type="checkbox" id="builds-only-with-travis-yml"}} + <label for="builds-only-with-travis-yml">Build only commits with .travis.yml file</label> + </p> - <p> - {{settings-input checked=build_pushes type="checkbox" id="build-pushes"}} - <label for="build-pushes">Build pushes</label> - </p> + <p> + {{settings-input checked=build_pushes type="checkbox" id="build-pushes"}} + <label for="build-pushes">Build pushes</label> + </p> - <p> - {{settings-input checked=build_pull_requests type="checkbox" id="build-pull-requests"}} - <label for="build-pull-requests">Build pull requests</label> - </p> - - <p> - {{#if saving}} - <span class="saving">Saving</span> - {{else}} - <button type="submit">Submit</button> - {{/if}} - </p> - {{/settings-form}} - {{/travis-tab}} -{{/travis-tabs}} + <p> + {{settings-input checked=build_pull_requests type="checkbox" id="build-pull-requests"}} + <label for="build-pull-requests">Build pull requests</label> + </p> + <p> + {{#if saving}} + <span class="saving">Saving</span> + {{else}} + <button type="submit">Submit</button> + {{/if}} + </p> + {{/settings-form}} + {{/travis-tab}} + {{/travis-tabs}} +</div> From c8cc13df5906780161151cce1f1c455c73b26e3b Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Wed, 8 Jan 2014 12:17:57 +0100 Subject: [PATCH 09/11] Change submit to save --- assets/scripts/app/templates/repo/settings.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scripts/app/templates/repo/settings.hbs b/assets/scripts/app/templates/repo/settings.hbs index e721c254..80737cf4 100644 --- a/assets/scripts/app/templates/repo/settings.hbs +++ b/assets/scripts/app/templates/repo/settings.hbs @@ -23,7 +23,7 @@ {{#if saving}} <span class="saving">Saving</span> {{else}} - <button type="submit">Submit</button> + <button type="submit">Save</button> {{/if}} </p> {{/settings-form}} From 357b176f93085bf55311301fa2c07e675dda1e09 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki <drogus@gmail.com> Date: Tue, 21 Jan 2014 18:46:36 +0100 Subject: [PATCH 10/11] Use switches on settings pane --- assets/scripts/app/components.coffee | 16 ++++++++++++--- assets/scripts/app/controllers.coffee | 2 +- .../templates/components/travis-switch.hbs | 2 +- .../scripts/app/templates/repo/settings.hbs | 20 ++++++------------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/assets/scripts/app/components.coffee b/assets/scripts/app/components.coffee index da423edd..09a177fb 100644 --- a/assets/scripts/app/components.coffee +++ b/assets/scripts/app/components.coffee @@ -1,9 +1,19 @@ Travis.TravisSwitchComponent = Ember.Component.extend tagName: 'a' classNames: ['travis-switch'] - classNameBindings: ['active'] + classNameBindings: ['_active:active'] - activeBinding: 'target.active' + # TODO: how to handle overriding properties to + # avoid naming it _action? + _active: (-> + @get('target.active') || @get('active') + ).property('target.active', 'active') click: -> - @sendAction('action', @get('target')) + if target = @get('target') + @set('target.active', !@get('target.active')) + else + @set('active', !@get('active')) + # allow for bindings to propagate + Ember.run.next this, -> + @sendAction('action', target) diff --git a/assets/scripts/app/controllers.coffee b/assets/scripts/app/controllers.coffee index 5a267beb..84fa90e7 100644 --- a/assets/scripts/app/controllers.coffee +++ b/assets/scripts/app/controllers.coffee @@ -54,7 +54,7 @@ Travis.RepoSettingsController = Em.ObjectController.extend tab: Ember.computed.alias('controllers.repoSettingsTab.model.tab') settings: Ember.computed.alias('model.settings') - submit: -> + save: -> @set('saving', true) self = this @get('model').saveSettings(@get('settings')).then -> diff --git a/assets/scripts/app/templates/components/travis-switch.hbs b/assets/scripts/app/templates/components/travis-switch.hbs index e7b20837..18617602 100644 --- a/assets/scripts/app/templates/components/travis-switch.hbs +++ b/assets/scripts/app/templates/components/travis-switch.hbs @@ -1,4 +1,4 @@ -{{#if active}} +{{#if _active}} ON {{else}} OFF diff --git a/assets/scripts/app/templates/repo/settings.hbs b/assets/scripts/app/templates/repo/settings.hbs index 80737cf4..db445f93 100644 --- a/assets/scripts/app/templates/repo/settings.hbs +++ b/assets/scripts/app/templates/repo/settings.hbs @@ -5,26 +5,18 @@ {{#travis-tab "general" "General Settings"}} {{#settings-form}} <p> - {{settings-input checked=builds_only_with_travis_yml type="checkbox" id="builds-only-with-travis-yml"}} - <label for="builds-only-with-travis-yml">Build only commits with .travis.yml file</label> + {{travis-switch action="save" active=settings.builds_only_with_travis_yml}} + Build only commits with .travis.yml file </p> <p> - {{settings-input checked=build_pushes type="checkbox" id="build-pushes"}} - <label for="build-pushes">Build pushes</label> + {{travis-switch action="save" active=settings.build_pushes}} + Build pushes </p> <p> - {{settings-input checked=build_pull_requests type="checkbox" id="build-pull-requests"}} - <label for="build-pull-requests">Build pull requests</label> - </p> - - <p> - {{#if saving}} - <span class="saving">Saving</span> - {{else}} - <button type="submit">Save</button> - {{/if}} + {{travis-switch action="save" active=settings.build_pull_requests}} + Build pull requests </p> {{/settings-form}} {{/travis-tab}} From 7d9db8cbaef33e3aa9bfcc263a8d52b23025a962 Mon Sep 17 00:00:00 2001 From: Justine Arreche <justine@neo.com> Date: Fri, 24 Jan 2014 10:04:12 -0500 Subject: [PATCH 11/11] restyled general settings --- assets/scripts/app/templates/repo/settings.hbs | 12 ++++++------ assets/styles/components/travis-switch.sass | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/assets/scripts/app/templates/repo/settings.hbs b/assets/scripts/app/templates/repo/settings.hbs index db445f93..1e515d4d 100644 --- a/assets/scripts/app/templates/repo/settings.hbs +++ b/assets/scripts/app/templates/repo/settings.hbs @@ -4,19 +4,19 @@ {{#travis-tabs}} {{#travis-tab "general" "General Settings"}} {{#settings-form}} - <p> - {{travis-switch action="save" active=settings.builds_only_with_travis_yml}} + <p class="settings-row"> Build only commits with .travis.yml file + {{travis-switch action="save" active=settings.builds_only_with_travis_yml}} </p> - <p> - {{travis-switch action="save" active=settings.build_pushes}} + <p class="settings-row"> Build pushes + {{travis-switch action="save" active=settings.build_pushes}} </p> - <p> - {{travis-switch action="save" active=settings.build_pull_requests}} + <p class="settings-row"> Build pull requests + {{travis-switch action="save" active=settings.build_pull_requests}} </p> {{/settings-form}} {{/travis-tab}} diff --git a/assets/styles/components/travis-switch.sass b/assets/styles/components/travis-switch.sass index 342aaac6..5b7ea12e 100644 --- a/assets/styles/components/travis-switch.sass +++ b/assets/styles/components/travis-switch.sass @@ -1,6 +1,10 @@ +.settings-row + margin-top: 20px + .travis-switch position: relative - display: block + display: inline-block + float: left width: 60px height: 18px margin: 0 10px 0 15px