Add support for themes (fixes #1925)

This commit is contained in:
Audrius Butkevicius 2016-01-10 15:37:31 +00:00
parent 6c0a973ac3
commit cd54186113
128 changed files with 205 additions and 138 deletions

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()

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 {

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -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 () {

View File

@ -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">

Some files were not shown because too many files have changed in this diff Show More