1<script>
2import { GlButton } from '@gitlab/ui';
3import createFlash, { FLASH_TYPES } from '~/flash';
4import { INTEGRATION_VIEW_CONFIGS, i18n } from '../constants';
5import IntegrationView from './integration_view.vue';
6
7function updateClasses(bodyClasses = '', applicationTheme, layout) {
8  // Remove body class for any previous theme, re-add current one
9  document.body.classList.remove(...bodyClasses.split(' '));
10  document.body.classList.add(applicationTheme);
11
12  // Toggle container-fluid class
13  if (layout === 'fluid') {
14    document
15      .querySelector('.content-wrapper .container-fluid')
16      .classList.remove('container-limited');
17  } else {
18    document.querySelector('.content-wrapper .container-fluid').classList.add('container-limited');
19  }
20}
21
22export default {
23  name: 'ProfilePreferences',
24  components: {
25    IntegrationView,
26    GlButton,
27  },
28  inject: {
29    integrationViews: {
30      default: [],
31    },
32    themes: {
33      default: [],
34    },
35    userFields: {
36      default: {},
37    },
38    formEl: 'formEl',
39    profilePreferencesPath: 'profilePreferencesPath',
40    bodyClasses: 'bodyClasses',
41  },
42  integrationViewConfigs: INTEGRATION_VIEW_CONFIGS,
43  i18n,
44  data() {
45    return {
46      isSubmitEnabled: true,
47      darkModeOnCreate: null,
48      darkModeOnSubmit: null,
49    };
50  },
51  computed: {
52    applicationThemes() {
53      return this.themes.reduce((themes, theme) => {
54        const { id, ...rest } = theme;
55        return { ...themes, [id]: rest };
56      }, {});
57    },
58  },
59  created() {
60    this.formEl.addEventListener('ajax:beforeSend', this.handleLoading);
61    this.formEl.addEventListener('ajax:success', this.handleSuccess);
62    this.formEl.addEventListener('ajax:error', this.handleError);
63    this.darkModeOnCreate = this.darkModeSelected();
64  },
65  beforeDestroy() {
66    this.formEl.removeEventListener('ajax:beforeSend', this.handleLoading);
67    this.formEl.removeEventListener('ajax:success', this.handleSuccess);
68    this.formEl.removeEventListener('ajax:error', this.handleError);
69  },
70  methods: {
71    darkModeSelected() {
72      const theme = this.getSelectedTheme();
73      return theme ? theme.css_class === 'gl-dark' : null;
74    },
75    getSelectedTheme() {
76      const themeId = new FormData(this.formEl).get('user[theme_id]');
77      return this.applicationThemes[themeId] ?? null;
78    },
79    handleLoading() {
80      this.isSubmitEnabled = false;
81      this.darkModeOnSubmit = this.darkModeSelected();
82    },
83    handleSuccess(customEvent) {
84      // Reload the page if the theme has changed from light to dark mode or vice versa
85      // to correctly load all required styles.
86      const modeChanged = this.darkModeOnCreate ? !this.darkModeOnSubmit : this.darkModeOnSubmit;
87      if (modeChanged) {
88        window.location.reload();
89        return;
90      }
91      updateClasses(this.bodyClasses, this.getSelectedTheme().css_class, this.selectedLayout);
92      const { message = this.$options.i18n.defaultSuccess, type = FLASH_TYPES.NOTICE } =
93        customEvent?.detail?.[0] || {};
94      createFlash({ message, type });
95      this.isSubmitEnabled = true;
96    },
97    handleError(customEvent) {
98      const { message = this.$options.i18n.defaultError, type = FLASH_TYPES.ALERT } =
99        customEvent?.detail?.[0] || {};
100      createFlash({ message, type });
101      this.isSubmitEnabled = true;
102    },
103  },
104};
105</script>
106
107<template>
108  <div class="row gl-mt-3 js-preferences-form">
109    <div v-if="integrationViews.length" class="col-sm-12">
110      <hr data-testid="profile-preferences-integrations-rule" />
111    </div>
112    <div v-if="integrationViews.length" class="col-lg-4 profile-settings-sidebar">
113      <h4 class="gl-mt-0" data-testid="profile-preferences-integrations-heading">
114        {{ $options.i18n.integrations }}
115      </h4>
116      <p>
117        {{ $options.i18n.integrationsDescription }}
118      </p>
119    </div>
120    <div v-if="integrationViews.length" class="col-lg-8">
121      <integration-view
122        v-for="view in integrationViews"
123        :key="view.name"
124        :help-link="view.help_link"
125        :message="view.message"
126        :message-url="view.message_url"
127        :config="$options.integrationViewConfigs[view.name]"
128      />
129    </div>
130    <div class="col-sm-12">
131      <hr />
132      <gl-button
133        category="primary"
134        variant="confirm"
135        name="commit"
136        type="submit"
137        :disabled="!isSubmitEnabled"
138        :value="$options.i18n.saveChanges"
139      >
140        {{ $options.i18n.saveChanges }}
141      </gl-button>
142    </div>
143  </div>
144</template>
145