mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-22 19:08:58 +00:00
Add support for themes (fixes #1925)
This commit is contained in:
parent
6c0a973ac3
commit
cd54186113
4
build.go
4
build.go
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
@ -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 () {
|
@ -136,8 +136,17 @@
|
|||||||
<span class="fa fa-repeat"></span> <span translate>Generate</span>
|
<span class="fa fa-repeat"></span> <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>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user