Merge pull request #2682 from AudriusButkevicius/themes

Add theme support
This commit is contained in:
Jakob Borg committed 2016-01-10 19:04:57 +01:00
commit 01c70caa8f
132 files changed
+688 -142

No files matched your search

+1
View File
@@ -2,6 +2,7 @@
Aaron Bieber <qbit@deftly.net> Aaron Bieber <qbit@deftly.net>
Adam Piggott <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com> Adam Piggott <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com>
Alessandro G. <alessandro.g89@gmail.com>
Alexander Graf <register-github@alex-graf.de> Alexander Graf <register-github@alex-graf.de>
Anderson Mesquita <andersonvom@gmail.com> Anderson Mesquita <andersonvom@gmail.com>
Andrew Dunham <andrew@du.nham.ca> Andrew Dunham <andrew@du.nham.ca>
+1
View File
@@ -2,6 +2,7 @@
acogdev <jake@acogdev.com> acogdev <jake@acogdev.com>
alex2108 <register-github@alex-graf.de> alex2108 <register-github@alex-graf.de>
alessandro.g89 <alessandro.g89@gmail.com>
andersonvom <andersonvom@gmail.com> andersonvom <andersonvom@gmail.com>
andrew-d <andrew@du.nham.ca> andrew-d <andrew@du.nham.ca>
asdil12 <dominik@heidler.eu> asdil12 <dominik@heidler.eu>
+2 -2
View File
@@ -410,7 +410,7 @@ func xdr() {
} }
func translate() { func translate() {
os.Chdir("gui/assets/lang") os.Chdir("gui/default/assets/lang")
runPipe("lang-en-new.json", "go", "run", "../../../script/translate.go", "lang-en.json", "../../") runPipe("lang-en-new.json", "go", "run", "../../../script/translate.go", "lang-en.json", "../../")
os.Remove("lang-en.json") os.Remove("lang-en.json")
err := os.Rename("lang-en-new.json", "lang-en.json") err := os.Rename("lang-en-new.json", "lang-en.json")
@@ -421,7 +421,7 @@ func translate() {
} }
func transifex() { func transifex() {
os.Chdir("gui/assets/lang") os.Chdir("gui/default/assets/lang")
runPrint("go", "run", "../../../script/transifexdl.go") runPrint("go", "run", "../../../script/transifexdl.go")
os.Chdir("../../..") os.Chdir("../../..")
assets() assets()
+60 -11
View File
@@ -52,6 +52,7 @@ type apiService struct {
id protocol.DeviceID id protocol.DeviceID
cfg *config.Wrapper cfg *config.Wrapper
assetDir string assetDir string
themes []string
model *model.Model model *model.Model
eventSub *events.BufferedSubscription eventSub *events.BufferedSubscription
discoverer *discover.CachingMux discoverer *discover.CachingMux
@@ -79,6 +80,15 @@ func newAPIService(id protocol.DeviceID, cfg *config.Wrapper, assetDir string, m
systemLog: systemLog, systemLog: systemLog,
} }
seen := make(map[string]struct{})
for file := range auto.Assets() {
theme := strings.Split(file, "/")[0]
if _, ok := seen[theme]; !ok {
seen[theme] = struct{}{}
service.themes = append(service.themes, theme)
}
}
var err error var err error
service.listener, err = service.getListener(cfg.GUI()) service.listener, err = service.getListener(cfg.GUI())
return service, err return service, err
@@ -198,10 +208,16 @@ func (s *apiService) Serve() {
mux.HandleFunc("/qr/", s.getQR) mux.HandleFunc("/qr/", s.getQR)
// Serve compiled in assets unless an asset directory was set (for development) // Serve compiled in assets unless an asset directory was set (for development)
mux.Handle("/", embeddedStatic{ assets := &embeddedStatic{
assetDir: s.assetDir, theme: s.cfg.GUI().Theme,
assets: auto.Assets(), lastModified: time.Now(),
}) mut: sync.NewRWMutex(),
assetDir: s.assetDir,
assets: auto.Assets(),
}
mux.Handle("/", assets)
s.cfg.Subscribe(assets)
guiCfg := s.cfg.GUI() guiCfg := s.cfg.GUI()
@@ -690,6 +706,7 @@ func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) {
res["pathSeparator"] = string(filepath.Separator) res["pathSeparator"] = string(filepath.Separator)
res["uptime"] = int(time.Since(startTime).Seconds()) res["uptime"] = int(time.Since(startTime).Seconds())
res["startTime"] = startTime res["startTime"] = startTime
res["themes"] = s.themes
sendJSON(w, res) sendJSON(w, res)
} }
@@ -1015,8 +1032,11 @@ func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
} }
type embeddedStatic struct { type embeddedStatic struct {
assetDir string theme string
assets map[string][]byte lastModified time.Time
mut sync.RWMutex
assetDir string
assets map[string][]byte
} }
func (s embeddedStatic) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s embeddedStatic) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -1039,13 +1059,21 @@ func (s embeddedStatic) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
bs, ok := s.assets[file] s.mut.RLock()
theme := s.theme
modified := s.lastModified
s.mut.RUnlock()
bs, ok := s.assets[theme+"/"+file]
if !ok { if !ok {
http.NotFound(w, r) bs, ok = s.assets[config.DefaultTheme+"/"+file]
return if !ok {
http.NotFound(w, r)
return
}
} }
if r.Header.Get("If-Modified-Since") == auto.AssetsBuildDate { if modifiedSince, err := time.Parse(r.Header.Get("If-Modified-Since"), http.TimeFormat); err == nil && modified.Before(modifiedSince) {
w.WriteHeader(http.StatusNotModified) w.WriteHeader(http.StatusNotModified)
return return
} }
@@ -1064,7 +1092,7 @@ func (s embeddedStatic) ServeHTTP(w http.ResponseWriter, r *http.Request) {
gr.Close() gr.Close()
} }
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(bs))) w.Header().Set("Content-Length", fmt.Sprintf("%d", len(bs)))
w.Header().Set("Last-Modified", auto.AssetsBuildDate) w.Header().Set("Last-Modified", modified.Format(http.TimeFormat))
w.Header().Set("Cache-Control", "public") w.Header().Set("Cache-Control", "public")
w.Write(bs) w.Write(bs)
@@ -1097,6 +1125,27 @@ func (s embeddedStatic) mimeTypeForFile(file string) string {
} }
} }
// VerifyConfiguration implements the config.Committer interface
func (s *embeddedStatic) VerifyConfiguration(from, to config.Configuration) error {
return nil
}
// CommitConfiguration implements the config.Committer interface
func (s *embeddedStatic) CommitConfiguration(from, to config.Configuration) bool {
s.mut.Lock()
if s.theme != to.GUI.Theme {
s.theme = to.GUI.Theme
s.lastModified = time.Now()
}
s.mut.Unlock()
return true
}
func (s *embeddedStatic) String() string {
return fmt.Sprintf("embeddedStatic@%p", s)
}
func (s *apiService) toNeedSlice(fs []db.FileInfoTruncated) []jsonDBFileInfo { func (s *apiService) toNeedSlice(fs []db.FileInfoTruncated) []jsonDBFileInfo {
res := make([]jsonDBFileInfo, len(fs)) res := make([]jsonDBFileInfo, len(fs))
for i, f := range fs { for i, f := range fs {
+475
View File
@@ -0,0 +1,475 @@
/*
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
*/
body {
padding-bottom: 70px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5 {
font-family: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.25;
}
ul+h5 {
margin-top: 1.5em;
}
#content {
margin-bottom: 50px;
}
.panel-progress {
background: #3498db;
height: 3px;
left: 0;
position: absolute;
top: 0;
display: block;
}
.panel-title {
position: relative;
text-overflow: ellipsis;
overflow: hidden;
}
identicon {
display: inline-block;
position: relative;
width: 1em;
height: 1em;
line-height: 1;
margin-right: 5px;
}
.identicon {
width: 1em;
height: 1em;
}
.identicon rect {
fill: #333;
}
.checkbox {
margin-top: 0px;
}
.checkbox input[type="checkbox"], .radio input[type="radio"] {
float: none; /* issue #1197 */
}
.popover {
max-width: none;
min-width: 250px;
}
.panel-heading .fa, .modal-header .fa {
margin-right: 10px;
}
.panel-heading {
position: relative;
overflow: hidden;
}
.text-monospace {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
.table-condensed>thead>tr>th, .table-condensed>tbody>tr>th, .table-condensed>tfoot>tr>th, .table-condensed>thead>tr>td, .table-condensed>tbody>tr>td, .table-condensed>tfoot>tr>td {
border-top: none;
}
.logo {
margin: 0;
padding: 0;
top: -5px;
position: relative;
}
.list-no-bullet {
list-style-type: none
}
.li-column {
display: inline-block;
min-width: 7em;
margin-right: 1em;
background-color: rgb(236, 240, 241);
border-radius: 3px;
padding: 1px 4px;
margin: 2px 2px;
}
.li-column span.data {
margin-left: 0.5em;
min-width: 10em;
text-align: right;
display: inline-block;
}
.ng-cloak {
display: none !important;
}
.table th {
white-space: nowrap;
font-weight: 400;
}
.table td {
padding-left: 20px !important;
}
.table td.small-data {
white-space: nowrap;
}
table.table-condensed {
table-layout: fixed;
}
table.table-condensed td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@media (max-width:767px) {
table.table-condensed td {
/* for mobile phones to allow linebreaks in long repro folder/shared with
* columns. */
white-space: normal;
}
}
.navbar-right {
/* to align with panel */
padding-right: 15px;
}
/**
* Menu for select language
*/
@media (min-width:480px) and (max-width:649px) {
*[language-select] > .dropdown-menu {
width: 230px;
}
}
@media (min-width:650px) {
*[language-select] > .dropdown-menu > li {
width: 50%;
float: left;
}
*[language-select] > .dropdown-menu {
width: 440px;
}
}
@media (max-width:479px) {
.dropdown-menu {
padding-top: 55px;
}
nav .dropdown-toggle {
font-size: 14px;
}
.dropdown-toggle {
float: left;
}
.navbar-brand {
padding-left: 0;
padding-top: 16px;
}
.navbar-nav .open .dropdown-menu > li > a {
padding: 12px 15px 12px 25px;
}
}
.panel-body .table-condensed {
margin-bottom: 0;
}
.dl-horizontal.dl-narrow dt {
width: 40px;
}
.dl-horizontal.dl-narrow dd {
margin-left: 60px;
}
/**
* Progress bars with centered text
*/
.progress {
margin-bottom: 0px;
position: relative;
}
.progress span.frontal {
text-align: center;
position: absolute;
display: block;
width: 100%;
}
.three-columns {
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
}
.two-columns {
-webkit-column-count: 2;
-moz-column-count: 2;
column-count: 2;
}
ul.three-columns li, ul.two-columns li {
padding-left: 0.5em;
text-indent: -0.5em;
}
/** Footer nav on small devices **/
@media (max-width: 1199px) {
body {
padding-bottom: 0;
}
.navbar-fixed-bottom {
position: static;
}
}
/**
Dark theme customizations start here.
Author: alessandro.g89
Source: https://userstyles.org/styles/122502/syncthing-dark
**/
body {
color: #aaa !important;
background-color: black !important;
}
a:hover,a:focus,a.focus{
outline: none !important;
}
/* navbar */
.navbar {
background-color: #333 !important;
border-color: #333 !important;
border-width: 2px !important;
}
.navbar-text, .dropdown>a, .dropdown-menu>li>a, .hidden-xs>a, .navbar-link {
color: #aaa !important;
}
.dropdown-menu {
border-color: #333 !important;
border-width: 2px !important;
background-color: #222 !important;
}
.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {
color: #fff !important;
background-color: #333 !important;
}
.open>.dropdown-toggle, .dropdown-toggle:hover {
border-color: #333 !important;
background-color: #222 !important;
}
.divider {
background-color: #333 !important;
height: 2px !important;
}
li.hidden-xs:hover, .navbar-link:hover, .navbar-link:focus {
outline: none !important;
border-color: #333 !important;
background-color: #222 !important;
}
.dropdown-menu>.active>a {
color: #fff !important;
background-color: #217dbb !important;
}
/* main panel */
.panel {
background-color: #111 !important;
border-width: 2px !important;
}
.panel-default {
border-color: #222 !important;
}
.panel-default>.panel-heading {
color: #aaa !important;
border-color: #222 !important;
background-color: #222 !important;
}
.panel-footer {
background-color: #111 !important;
border-width: 0 !important;
}
.table-striped>tbody>tr:nth-of-type(odd) {
background-color: #181818 !important;
}
.panel-group .panel-heading+.panel-collapse>.panel-body, .panel-group .panel-heading+.panel-collapse>.list-group {
border-top: 1px solid #222 !important;
}
.identicon>rect {
fill: #aaa !important;
}
/* buttons */
.btn {
border-radius: 3px !important;
border-width: 0px !important;
}
.btn:hover, .btn:focus, .btn.focus {
outline: none !important;
}
.btn-default {
color: #aaa !important;
background-color: #333 !important;
}
.btn-default:hover, .btn-default:focus, .btn-default.focus {
color: #fff !important;
background-color: #484848 !important;
}
.btn-primary {
background-color: #217dbb !important;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary.focus {
background-color: #3498db !important;
}
.btn-warning {
background-color: #c29d0b !important;
}
.btn-warning:hover, .btn-warning:focus, .btn-warning.focus {
background-color: #f1c40f !important;
}
.btn-danger {
background-color: #d62c1a !important;
}
.btn-danger:hover, .btn-danger:focus, .btn-danger.focus {
background-color: #e74c3c !important;
}
/* modal dialogs */
.modal-header {
border-color: #222 !important;
background-color: #222;
}
.modal-content {
border-color: #666 !important;
border-width: 2px !important;
background-color: #111 !important;
}
.modal-footer {
border-color: #111 !important;
background-color: #111 !important;
}
.alert-warning {
background-color: #c29d0b !important;
}
.alert-danger {
background-color: #d62c1a !important;
}
.help-block {
color: #aaa !important;
}
.form-control {
color: #aaa !important;
border-color: #444 !important;
background-color: black !important;
}
code.ng-binding{
color: #f99 !important;
background-color: #444 !important;
}
.well, .form-control[readonly="readonly"] { /* read-only fields*/
color: #444 !important;
border-color: #444 !important;
background-color: #111 !important;
}
/* buttons for pagination */
.pagination>li>a, .pagination>li>span {
background-color: #333 !important;
border-color: #484848 !important;
}
.pagination>li>a:hover, .pagination>li>a:focus, .pagination>li>a.focus {
background-color: #484848 !important;
}
/* progess bars */
.progress-bar {
background-color: #217dbb !important;
}
.progress-bar-success {
background-color: #0A8522 !important;
}
.progress-bar-info {
background-color: #9b59b6 !important;
}
.progress-bar-warning {
background-color: #c29d0b !important;
}
.progress-bar-danger {
background-color: #d62c1a !important;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -14,6 +14,7 @@
<ul class="list-unstyled three-columns" id="contributor-list"> <ul class="list-unstyled three-columns" id="contributor-list">
<li class="auto-generated">Aaron Bieber</li> <li class="auto-generated">Aaron Bieber</li>
<li class="auto-generated">Adam Piggott</li> <li class="auto-generated">Adam Piggott</li>
<li class="auto-generated">Alessandro G.</li>
<li class="auto-generated">Alexander Graf</li> <li class="auto-generated">Alexander Graf</li>
<li class="auto-generated">Anderson Mesquita</li> <li class="auto-generated">Anderson Mesquita</li>
<li class="auto-generated">Andrew Dunham</li> <li class="auto-generated">Andrew Dunham</li>
File renamed without changes.
@@ -866,6 +866,7 @@ angular.module('syncthing.core')
$scope.saveSettings = function () { $scope.saveSettings = function () {
// Make sure something changed // Make sure something changed
var changed = !angular.equals($scope.config.options, $scope.tmpOptions) || !angular.equals($scope.config.gui, $scope.tmpGUI); var changed = !angular.equals($scope.config.options, $scope.tmpOptions) || !angular.equals($scope.config.gui, $scope.tmpGUI);
var themeChanged = $scope.config.gui.theme !== $scope.tmpGUI.theme;
if (changed) { if (changed) {
// Check if usage reporting has been enabled or disabled // Check if usage reporting has been enabled or disabled
if ($scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted <= 0) { if ($scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted <= 0) {
@@ -901,6 +902,10 @@ angular.module('syncthing.core')
} }
$('#settings').modal("hide"); $('#settings').modal("hide");
if (themeChanged) {
document.location.reload(true);
}
}; };
$scope.saveAdvanced = function () { $scope.saveAdvanced = function () {
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -136,8 +136,17 @@
<span class="fa fa-repeat"></span>&nbsp;<span translate>Generate</span> <span class="fa fa-repeat"></span>&nbsp;<span translate>Generate</span>
</button> </button>
</div> </div>
</div>
<div class="form-group" ng-if="system.themes.length > 1">
<label>GUI Theme</label>
<select class="form-control" ng-model="tmpGUI.theme" ng->
<option ng-repeat="theme in system.themes" value="{{ theme }}">
{{ theme.charAt(0).toUpperCase() + theme.slice(1) }}
</option>
</select>
</div>
</div>
</div> </div>
</form> </form>
</div> </div>
File renamed without changes.
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more