Repository: syncthing/syncthing
File: gui/default/syncthing/core/syncthingController.js (JavaScript)

Code before:
                // Apply new settings locally
                $scope.thisDeviceIn($scope.tmpDevices).name = $scope.tmpOptions.deviceName;
                $scope.config.options = angular.copy($scope.tmpOptions);
                $scope.config.gui = angular.copy($scope.tmpGUI);
                $scope.config.remoteIgnoredDevices = angular.copy($scope.tmpRemoteIgnoredDevices);
                $scope.config.devices = angular.copy($scope.tmpDevices);
                // $scope.devices is updated by updateLocalConfig based on
                // the config changed event, but settingsModified will look
                // at it before that and conclude that the settings are
                // modified (even though we just saved) unless we update
                // here as well...
                $scope.devices = deviceMap($scope.config.devices);

                $('#settings').modal('hide').on('hidden.modal.bs', function () {
                    $scope.saveConfig().then(function () {
                        if (themeChanged) {
                            document.location.reload(true);
                        }
                    });
                });
            } else {
                $('#settings').modal('hide');
            }
        };

        $scope.saveAdvanced = function () {
            $scope.config = $scope.advancedConfig;
            $('#advanced').modal('hide').one('hidden.bs.modal', function () {
                $scope.saveConfig();
            });
        };

        $scope.restart = function () {
            restarting = true;
            $('#restarting').modal();
            $http.post(urlbase + '/system/restart');
            $scope.configInSync = true;

            // Switch webpage protocol if needed
            if ($scope.protocolChanged) {
                var protocol = 'http';

                if ($scope.config.gui.useTLS) {
                    protocol = 'https';
                }

                setTimeout(function () {
                    window.location.protocol = protocol;
                }, 2500);

                $scope.protocolChanged = false;

Review comment (bug):

I don't quite understand this part. Is anyone able to explain why `$("#settings").off("hide.bs.modal").modal("hide")` is used here instead of a simple `$("#settings").modal("hide")`?


Got it. It's because there is an event attached when opening Settings in

https://github.com/syncthing/syncthing/blob/38bbdebffa74df9e5937749a3780d39b24adc331/gui/default/syncthing/core/syncthingController.js#L1505-L1512

for the confirmation window to pop-up, and it still needs to be cleared upon saving changes.

**Edit 1**:

Actually `off("hide.bs.modal")` is superfluous here because the same is already done in

https://github.com/syncthing/syncthing/blob/38bbdebffa74df9e5937749a3780d39b24adc331/gui/default/syncthing/core/syncthingController.js#L1510

**Edit 2**:

I've now re-added the thing in https://github.com/syncthing/syncthing/pull/9078/commits/0e94c8b2fe5cc04a6a320b45a169ae18c41a1095, as otherwise there seems be a race condition where the modal gets hidden faster than the hide handler is removed, so that the confirmation pop-up shows up even when saving settings (and not just trying to close the Settings modal unsaved).

Code after:
                // Check if protocol will need to be changed on restart
                if ($scope.config.gui.useTLS !== $scope.tmpGUI.useTLS) {
                    $scope.protocolChanged = true;
                }

                // Parse strings to arrays before copying over
                ['listenAddresses', 'globalAnnounceServers'].forEach(function (key) {
                    $scope.tmpOptions[key] = $scope.tmpOptions["_" + key + "Str"].split(/[ ,]+/).map(function (x) {
                        return x.trim();
                    });
                });

                // Apply new settings locally
                $scope.thisDeviceIn($scope.tmpDevices).name = $scope.tmpOptions.deviceName;
                $scope.config.options = angular.copy($scope.tmpOptions);
                $scope.config.gui = angular.copy($scope.tmpGUI);
                $scope.config.remoteIgnoredDevices = angular.copy($scope.tmpRemoteIgnoredDevices);
                $scope.config.devices = angular.copy($scope.tmpDevices);
                // $scope.devices is updated by updateLocalConfig based on
                // the config changed event, but settingsModified will look
                // at it before that and conclude that the settings are
                // modified (even though we just saved) unless we update
                // here as well...
                $scope.devices = deviceMap($scope.config.devices);

                $scope.saveConfig().then(function () {
                    if (themeChanged) {
                        document.location.reload(true);
                    } else {
                        $('#settings').off('hide.bs.modal')
                        hideModal('#settings');
                    }
                });
            } else {
                $('#settings').off('hide.bs.modal')
                hideModal('#settings');
            }
        };

        $scope.saveAdvanced = function () {
            $scope.config = $scope.advancedConfig;
            $scope.saveConfig().then(function () {
                hideModal('#advanced');
            });
        };

        $scope.restart = function () {
            restarting = true;
            showModal('#restarting');
            $http.post(urlbase + '/system/restart');
            $scope.configInSync = true;