Add support for themes (fixes #1925)

This commit is contained in:
Audrius Butkevicius committed 2016-01-10 17:57:27 +00:00
1 parent 6c0a973ac3
commit cd54186113
128 files changed
+205 -138

No files matched your search

+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 {
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.
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 128 files, more files were not shown because too many files have changed in this diff. Show more