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
+198 -131

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()
+54 -5
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{
theme: s.cfg.GUI().Theme,
lastModified: time.Now(),
mut: sync.NewRWMutex(),
assetDir: s.assetDir, assetDir: s.assetDir,
assets: auto.Assets(), 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,6 +1032,9 @@ func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
} }
type embeddedStatic struct { type embeddedStatic struct {
theme string
lastModified time.Time
mut sync.RWMutex
assetDir string assetDir string
assets map[string][]byte assets map[string][]byte
} }
@@ -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 {
bs, ok = s.assets[config.DefaultTheme+"/"+file]
if !ok { if !ok {
http.NotFound(w, r) http.NotFound(w, r)
return 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,9 +136,18 @@
<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 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>
</div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
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