Reduce global variables pollution

This commit is contained in:
Pig Fang 2018-09-09 09:28:05 +08:00
parent 4cd1d91f39
commit 130d16a2f0
36 changed files with 101 additions and 99 deletions

View File

@ -78,4 +78,3 @@ rules:
globals: globals:
blessing: false blessing: false
__bs_data__: false

View File

@ -44,7 +44,7 @@ export default {
'red', 'red',
'black', 'black',
], ],
currentSkin: window.currentSkin currentSkin: blessing.extra.currentSkin
}; };
}, },
methods: { methods: {

View File

@ -94,7 +94,7 @@ export default {
captcha: '', captcha: '',
remember: false, remember: false,
time: Date.now(), time: Date.now(),
tooManyFails: __bs_data__.tooManyFails, tooManyFails: blessing.extra.tooManyFails,
infoMsg: '', infoMsg: '',
warningMsg: '', warningMsg: '',
pending: false, pending: false,

View File

@ -131,7 +131,7 @@ export default {
infoMsg: '', infoMsg: '',
warningMsg: '', warningMsg: '',
pending: false, pending: false,
requirePlayer: __bs_data__.player requirePlayer: blessing.extra.player
}), }),
methods: { methods: {
async submit() { async submit() {

View File

@ -159,11 +159,11 @@ export default {
size: 0, size: 0,
uploadAt: '', uploadAt: '',
public: true, public: true,
liked: __bs_data__.inCloset, liked: blessing.extra.inCloset,
canBeDownloaded: __bs_data__.download, canBeDownloaded: blessing.extra.download,
currentUid: __bs_data__.currentUid, currentUid: blessing.extra.currentUid,
admin: __bs_data__.admin, admin: blessing.extra.admin,
uploaderNickName: __bs_data__.nickname, uploaderNickName: blessing.extra.nickname,
}; };
}, },
computed: { computed: {

View File

@ -129,10 +129,10 @@ export default {
files: [], files: [],
texture: '', texture: '',
uploading: false, uploading: false,
textureNameRule: __bs_data__.rule, textureNameRule: blessing.extra.rule,
privacyNotice: __bs_data__.privacyNotice, privacyNotice: blessing.extra.privacyNotice,
scorePublic: __bs_data__.scorePublic, scorePublic: blessing.extra.scorePublic,
scorePrivate: __bs_data__.scorePrivate, scorePrivate: blessing.extra.scorePrivate,
}; };
}, },
computed: { computed: {

View File

@ -20,7 +20,7 @@ export default {
name: 'EmailVerification', name: 'EmailVerification',
data() { data() {
return { return {
verified: !__bs_data__.unverified, verified: !blessing.extra.unverified,
pending: false, pending: false,
}; };
}, },

View File

@ -279,8 +279,8 @@ export default {
alex: false, alex: false,
cape: false cape: false
}, },
playerNameRule: __bs_data__.rule, playerNameRule: blessing.extra.rule,
playerNameLength: __bs_data__.length playerNameLength: blessing.extra.length
}; };
}, },
beforeMount() { beforeMount() {

View File

@ -211,7 +211,7 @@ export default {
currentPassword: '', currentPassword: '',
deleteConfirm: '', deleteConfirm: '',
siteName: blessing.site_name, siteName: blessing.site_name,
isAdmin: __bs_data__.admin isAdmin: blessing.extra.admin
}), }),
methods: { methods: {
nl2br: str => str.replace(/\n/g, '<br>'), nl2br: str => str.replace(/\n/g, '<br>'),

View File

@ -17,6 +17,4 @@ export function emit(eventName, payload) {
bus[eventName] && bus[eventName].forEach(listener => listener(payload)); bus[eventName] && bus[eventName].forEach(listener => listener(payload));
} }
Object.defineProperty(window, 'bsEmitter', { blessing.event = { on, emit };
get: () => Object.freeze({ on, emit })
});

View File

@ -7,9 +7,9 @@ import Vue from 'vue';
* @param {object} parameters * @param {object} parameters
* @return {string} * @return {string}
*/ */
export function trans(key, parameters = {}) { export function trans(key, parameters = Object.create(null)) {
const segments = key.split('.'); const segments = key.split('.');
let temp = window.__bs_i18n__ || {}; let temp = blessing.i18n || Object.create(null);
for (const segment of segments) { for (const segment of segments) {
if (!temp[segment]) { if (!temp[segment]) {

View File

@ -69,4 +69,4 @@ Vue.use(_Vue => {
}; };
}); });
window.bsAjax = { get, post }; blessing.fetch = { get, post };

View File

@ -79,5 +79,5 @@ export const swal = sweetalert2.mixin({
}); });
window.toastr = toastr; window.toastr = toastr;
window.showModal = showModal;
window.swal = swal; window.swal = swal;
blessing.notify = { showMsg, showModal };

View File

@ -11,17 +11,28 @@ declare global {
timezone: string timezone: string
version: string version: string
route: string route: string
} extra: any
} i18n: object
interface Window { fetch: {
bsEmitter: { get(url: string, params?: object): Promise<object>
on(eventName: string, listener: Function): void post(url: string, data?: object): Promise<object>
emit(eventName: string, payload: object): void }
},
bsAjax: { event: {
get(url: string, params?: object): Promise<object> on(eventName: string, listener: Function): void
post(url: string, data?: object): Promise<object> emit(eventName: string, payload: object): void
}
notify: {
showMsg(message: string, type?: string): void
showModal(
message: string,
title?: string,
type?: string,
options?: Partial<{ btnText: string, callback: string, destroyOnClose: boolean }>
)
}
} }
} }

View File

@ -2,7 +2,7 @@ import Vue from 'vue';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import Customization from '@/components/admin/Customization'; import Customization from '@/components/admin/Customization';
window.currentSkin = 'skin-blue'; window.blessing.extra = { currentSkin: 'skin-blue' };
test('preview color', () => { test('preview color', () => {
document.body.classList.add('skin-blue'); document.body.classList.add('skin-blue');

View File

@ -6,13 +6,13 @@ import { swal } from '@/js/notify';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
test('show captcha if too many login fails', () => { test('show captcha if too many login fails', () => {
window.__bs_data__ = { tooManyFails: true }; window.blessing.extra = { tooManyFails: true };
const wrapper = mount(Login); const wrapper = mount(Login);
expect(wrapper.find('img').attributes('src')).toMatch(/\/auth\/captcha\?v=\d+/); expect(wrapper.find('img').attributes('src')).toMatch(/\/auth\/captcha\?v=\d+/);
}); });
test('click to refresh captcha', () => { test('click to refresh captcha', () => {
window.__bs_data__ = { tooManyFails: true }; window.blessing.extra = { tooManyFails: true };
jest.spyOn(Date, 'now'); jest.spyOn(Date, 'now');
const wrapper = mount(Login); const wrapper = mount(Login);
wrapper.find('img').trigger('click'); wrapper.find('img').trigger('click');
@ -20,7 +20,7 @@ test('click to refresh captcha', () => {
}); });
test('login', async () => { test('login', async () => {
window.__bs_data__ = { tooManyFails: false }; window.blessing.extra = { tooManyFails: false };
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: 'fail' }) .mockResolvedValueOnce({ errno: 1, msg: 'fail' })
.mockResolvedValueOnce({ errno: 1, login_fails: 4 }) .mockResolvedValueOnce({ errno: 1, login_fails: 4 })

View File

@ -5,7 +5,7 @@ import { swal } from '@/js/notify';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
window.__bs_data__ = { player: false }; window.blessing.extra = { player: false };
test('click to refresh captcha', () => { test('click to refresh captcha', () => {
jest.spyOn(Date, 'now'); jest.spyOn(Date, 'now');
@ -15,12 +15,12 @@ test('click to refresh captcha', () => {
}); });
test('require player name', () => { test('require player name', () => {
window.__bs_data__ = { player: true }; window.blessing.extra = { player: true };
const wrapper = mount(Register); const wrapper = mount(Register);
expect(wrapper.findAll('[type="text"]').at(0).attributes('placeholder')).toBe('auth.player-name'); expect(wrapper.findAll('[type="text"]').at(0).attributes('placeholder')).toBe('auth.player-name');
window.__bs_data__ = { player: false }; window.blessing.extra = { player: false };
}); });
test('register', async () => { test('register', async () => {
@ -94,7 +94,7 @@ test('register', async () => {
}); });
test('register with player name', async () => { test('register with player name', async () => {
window.__bs_data__ = { player: true }; window.blessing.extra = { player: true };
Vue.prototype.$http.post.mockResolvedValue({ errno: 0, msg: 'ok' }); Vue.prototype.$http.post.mockResolvedValue({ errno: 0, msg: 'ok' });
const wrapper = mount(Register); const wrapper = mount(Register);
const button = wrapper.find('button'); const button = wrapper.find('button');

View File

@ -7,7 +7,7 @@ import toastr from 'toastr';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
window.__bs_data__ = { window.blessing.extra = {
download: true, download: true,
currentUid: 0, currentUid: 0,
admin: false, admin: false,
@ -35,7 +35,7 @@ test('button for adding to closet should be disabled if not auth', () => {
test('button for adding to closet should be disabled if auth', () => { test('button for adding to closet should be disabled if auth', () => {
Vue.prototype.$http.get.mockResolvedValue({}); Vue.prototype.$http.get.mockResolvedValue({});
Object.assign(window.__bs_data__, { inCloset: true, currentUid: 1 }); Object.assign(window.blessing.extra, { inCloset: true, currentUid: 1 });
const wrapper = mount(Show, { const wrapper = mount(Show, {
mocks: { mocks: {
$route: ['/skinlib/show/1', '1'] $route: ['/skinlib/show/1', '1']
@ -82,7 +82,7 @@ test('render basic information', async () => {
}); });
test('render action text of editing texture name', async () => { test('render action text of editing texture name', async () => {
Object.assign(window.__bs_data__, { admin: true }); Object.assign(window.blessing.extra, { admin: true });
Vue.prototype.$http.get.mockResolvedValue({ uploader: 1, name: 'name' }); Vue.prototype.$http.get.mockResolvedValue({ uploader: 1, name: 'name' });
let wrapper = mount(Show, { let wrapper = mount(Show, {
@ -93,7 +93,7 @@ test('render action text of editing texture name', async () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.contains('small')).toBeTrue(); expect(wrapper.contains('small')).toBeTrue();
Object.assign(window.__bs_data__, { currentUid: 2, admin: false }); Object.assign(window.blessing.extra, { currentUid: 2, admin: false });
wrapper = mount(Show, { wrapper = mount(Show, {
mocks: { mocks: {
$route: ['/skinlib/show/1', '1'] $route: ['/skinlib/show/1', '1']
@ -104,7 +104,7 @@ test('render action text of editing texture name', async () => {
}); });
test('render nickname of uploader', () => { test('render nickname of uploader', () => {
Object.assign(window.__bs_data__, { nickname: null }); Object.assign(window.blessing.extra, { nickname: null });
Vue.prototype.$http.get.mockResolvedValue({}); Vue.prototype.$http.get.mockResolvedValue({});
const wrapper = mount(Show, { const wrapper = mount(Show, {
mocks: { mocks: {
@ -115,7 +115,7 @@ test('render nickname of uploader', () => {
}); });
test('operation panel should not be rendered if not auth', () => { test('operation panel should not be rendered if not auth', () => {
Object.assign(window.__bs_data__, { currentUid: 0 }); Object.assign(window.blessing.extra, { currentUid: 0 });
Vue.prototype.$http.get.mockResolvedValue({}); Vue.prototype.$http.get.mockResolvedValue({});
const wrapper = mount(Show, { const wrapper = mount(Show, {
mocks: { mocks: {
@ -126,7 +126,7 @@ test('operation panel should not be rendered if not auth', () => {
}); });
test('link to downloading texture', async () => { test('link to downloading texture', async () => {
Object.assign(window.__bs_data__, { download: false }); Object.assign(window.blessing.extra, { download: false });
Vue.prototype.$http.get.mockResolvedValue({ hash: '123' }); Vue.prototype.$http.get.mockResolvedValue({ hash: '123' });
const wrapper = mount(Show, { const wrapper = mount(Show, {
mocks: { mocks: {
@ -139,7 +139,7 @@ test('link to downloading texture', async () => {
}); });
test('add to closet', async () => { test('add to closet', async () => {
Object.assign(window.__bs_data__, { currentUid: 1, inCloset: false }); Object.assign(window.blessing.extra, { currentUid: 1, inCloset: false });
Vue.prototype.$http.get.mockResolvedValue({ name: 'wow', likes: 2 }); Vue.prototype.$http.get.mockResolvedValue({ name: 'wow', likes: 2 });
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 1, msg: '1' })
@ -179,7 +179,7 @@ test('add to closet', async () => {
}); });
test('remove from closet', async () => { test('remove from closet', async () => {
Object.assign(window.__bs_data__, { currentUid: 1, inCloset: true }); Object.assign(window.blessing.extra, { currentUid: 1, inCloset: true });
Vue.prototype.$http.get.mockResolvedValue({ likes: 2 }); Vue.prototype.$http.get.mockResolvedValue({ likes: 2 });
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 1, msg: '1' })
@ -213,7 +213,7 @@ test('remove from closet', async () => {
}); });
test('change texture name', async () => { test('change texture name', async () => {
Object.assign(window.__bs_data__, { admin: true }); Object.assign(window.blessing.extra, { admin: true });
Vue.prototype.$http.get.mockResolvedValue({ name: 'old-name' }); Vue.prototype.$http.get.mockResolvedValue({ name: 'old-name' });
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 1, msg: '1' })

View File

@ -10,7 +10,7 @@ jest.mock('toastr');
jest.mock('@/js/notify'); jest.mock('@/js/notify');
jest.mock('@/js/net'); jest.mock('@/js/net');
window.__bs_data__ = { window.blessing.extra = {
textureNameRule: 'rule', textureNameRule: 'rule',
privacyNotice: 'privacyNotice', privacyNotice: 'privacyNotice',
scorePrivate: 10, scorePrivate: 10,

View File

@ -8,7 +8,7 @@ import { swal } from '@/js/notify';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
window.__bs_data__ = { unverified: false }; window.blessing.extra = { unverified: false };
test('fetch closet data before mount', () => { test('fetch closet data before mount', () => {
Vue.prototype.$http.get.mockResolvedValue({}); Vue.prototype.$http.get.mockResolvedValue({});

View File

@ -6,7 +6,7 @@ import { swal } from '@/js/notify';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
window.__bs_data__ = { unverified: false }; window.blessing.extra = { unverified: false };
function scoreInfo(data = {}) { function scoreInfo(data = {}) {
return { return {

View File

@ -6,13 +6,13 @@ import { swal } from '@/js/notify';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
test('message box should not be render if verified', () => { test('message box should not be render if verified', () => {
window.__bs_data__ = { unverified: false }; window.blessing.extra = { unverified: false };
const wrapper = mount(EmailVerification); const wrapper = mount(EmailVerification);
expect(wrapper.isEmpty()).toBeTrue(); expect(wrapper.isEmpty()).toBeTrue();
}); });
test('resend email', async () => { test('resend email', async () => {
window.__bs_data__ = { unverified: true }; window.blessing.extra = { unverified: true };
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 1, msg: '1' })
.mockResolvedValueOnce({ errno: 0, msg: '0' }); .mockResolvedValueOnce({ errno: 0, msg: '0' });

View File

@ -7,7 +7,7 @@ import { swal } from '@/js/notify';
jest.mock('toastr'); jest.mock('toastr');
jest.mock('@/js/notify'); jest.mock('@/js/notify');
window.__bs_data__ = { window.blessing.extra = {
rule: 'rule', rule: 'rule',
length: 'length' length: 'length'
}; };

View File

@ -7,14 +7,14 @@ import { swal } from '@/js/notify';
jest.mock('@/js/notify'); jest.mock('@/js/notify');
window.__bs_data__ = { unverified: false }; window.blessing.extra = { unverified: false };
test('computed values', () => { test('computed values', () => {
window.__bs_data__ = { admin: true }; window.blessing.extra = { admin: true };
const wrapper = mount(Profile); const wrapper = mount(Profile);
expect(wrapper.vm.siteName).toBe('Blessing Skin'); expect(wrapper.vm.siteName).toBe('Blessing Skin');
expect(wrapper.vm.isAdmin).toBeTrue(); expect(wrapper.vm.isAdmin).toBeTrue();
window.__bs_data__ = { admin: false }; window.blessing.extra = { admin: false };
expect(mount(Profile).vm.isAdmin).toBeFalse(); expect(mount(Profile).vm.isAdmin).toBeFalse();
}); });
@ -152,7 +152,7 @@ test('change email', async () => {
}); });
test('delete account', async () => { test('delete account', async () => {
window.__bs_data__ = { admin: true }; window.blessing.extra = { admin: true };
swal.mockResolvedValue(); swal.mockResolvedValue();
Vue.prototype.$http.post Vue.prototype.$http.post
.mockResolvedValueOnce({ errno: 1, msg: 'w' }) .mockResolvedValueOnce({ errno: 1, msg: 'w' })

View File

@ -6,7 +6,7 @@ test('mount to global', () => {
}); });
test('translate text', () => { test('translate text', () => {
window.__bs_i18n__ = { a: { b: { c: 'text', d: 'Hi, :name!' } } }; window.blessing.i18n = { a: { b: { c: 'text', d: 'Hi, :name!' } } };
expect(trans('a.b.c')).toBe('text'); expect(trans('a.b.c')).toBe('text');
expect(trans('a.b.d')).toBe('Hi, :name!'); expect(trans('a.b.d')).toBe('Hi, :name!');
expect(trans('a.b.d', { name: 'me' })).toBe('Hi, me!'); expect(trans('a.b.d', { name: 'me' })).toBe('Hi, me!');

View File

@ -1,3 +1,3 @@
import lang from './front-end.yml'; import lang from './front-end.yml';
window.__bs_i18n__ = Object.assign(window.__bs_i18n__ || {}, lang); blessing.i18n = Object.assign(blessing.i18n || Object.create(null), lang);

View File

@ -1,3 +1,3 @@
import lang from './front-end.yml'; import lang from './front-end.yml';
window.__bs_i18n__ = Object.assign(window.__bs_i18n__ || {}, lang); blessing.i18n = Object.assign(blessing.i18n || Object.create(null), lang);

View File

@ -37,7 +37,9 @@
</div><!-- /.content-wrapper --> </div><!-- /.content-wrapper -->
<script> <script>
var currentSkin = "{{ option('color_scheme') }}"; blessing.extra = {
currentSkin: "{{ option('color_scheme') }}"
};
</script> </script>
@endsection @endsection

View File

@ -25,11 +25,9 @@
<!-- /.login-box --> <!-- /.login-box -->
<script> <script>
Object.defineProperty(window, '__bs_data__', { Object.defineProperty(blessing, 'extra', {
configurable: false, configurable: false,
get: function () { get: () => Object.freeze({ tooManyFails: {{ cache(sha1('login_fails_'.get_client_ip())) > 3 ? 'true' : 'false' }} })
return Object.freeze({ tooManyFails: {{ cache(sha1('login_fails_'.get_client_ip())) > 3 ? 'true' : 'false' }} })
}
}) })
</script> </script>

View File

@ -19,12 +19,10 @@
<!-- /.register-box --> <!-- /.register-box -->
<script> <script>
Object.defineProperty(window, '__bs_data__', { Object.defineProperty(blessing, 'extra', {
get: function () { get: () => Object.freeze({
return Object.freeze({ player: {{ option('register_with_player_name') ? 'true' : 'false' }},
player: {{ option('register_with_player_name') ? 'true' : 'false' }}, }),
})
},
configurable: false configurable: false
}) })
</script> </script>

View File

@ -41,17 +41,15 @@
</div><!-- /.content-wrapper --> </div><!-- /.content-wrapper -->
<script> <script>
Object.defineProperty(window, '__bs_data__', { Object.defineProperty(blessing, 'extra', {
configurable: false, configurable: false,
get: function () { get: () => Object.freeze({
return Object.freeze({ download: {{ option('allow_downloading_texture') ? 'true' : 'false' }},
download: {{ option('allow_downloading_texture') ? 'true' : 'false' }}, currentUid: {{ is_null($user) ? '0' : $user->uid }},
currentUid: {{ is_null($user) ? '0' : $user->uid }}, admin: {{ $user && $user->isAdmin() ? 'true' : 'false' }},
admin: {{ $user && $user->isAdmin() ? 'true' : 'false' }}, inCloset: {{ $user && $user->getCloset()->has($texture->tid) ? 'true' : 'false' }},
inCloset: {{ $user && $user->getCloset()->has($texture->tid) ? 'true' : 'false' }}, nickname: @php echo ($up = app('users')->get($texture->uploader)) ? '"'.$up->nickname.'"' : 'null' @endphp
nickname: @php echo ($up = app('users')->get($texture->uploader)) ? '"'.$up->nickname.'"' : 'null' @endphp })
})
}
}) })
</script> </script>

View File

@ -19,7 +19,7 @@
</div><!-- /.content-wrapper --> </div><!-- /.content-wrapper -->
<script> <script>
var __bs_data__ = { blessing.extra = {
rule: "{{ option('texture_name_regexp') ? trans('skinlib.upload.name-rule-regexp', compact('regexp')) : trans('skinlib.upload.name-rule') }}", rule: "{{ option('texture_name_regexp') ? trans('skinlib.upload.name-rule-regexp', compact('regexp')) : trans('skinlib.upload.name-rule') }}",
privacyNotice: "@lang('skinlib.upload.private-score-notice', ['score' => option('private_score_per_storage')])", privacyNotice: "@lang('skinlib.upload.private-score-notice', ['score' => option('private_score_per_storage')])",
scorePublic: {{ option('score_per_storage') }}, scorePublic: {{ option('score_per_storage') }},

View File

@ -22,11 +22,10 @@
</div><!-- /.content-wrapper --> </div><!-- /.content-wrapper -->
<script> <script>
Object.defineProperty(window, '__bs_data__', { Object.defineProperty(blessing, 'extra', {
value: Object.freeze({ get: () => Object.freeze({
unverified: {{ option('require_verification') && !$user->verified ? 'true' : 'false' }} unverified: {{ option('require_verification') && !$user->verified ? 'true' : 'false' }}
}), })
writable: false
}) })
</script> </script>
@endsection @endsection

View File

@ -74,11 +74,10 @@
</div><!-- /.modal --> </div><!-- /.modal -->
<script> <script>
Object.defineProperty(window, '__bs_data__', { Object.defineProperty(blessing, 'extra', {
value: Object.freeze({ get: () => Object.freeze({
unverified: {{ option('require_verification') && !$user->verified ? 'true' : 'false' }} unverified: {{ option('require_verification') && !$user->verified ? 'true' : 'false' }}
}), })
writable: false
}) })
</script> </script>
@endsection @endsection

View File

@ -18,7 +18,7 @@
</div><!-- /.content-wrapper --> </div><!-- /.content-wrapper -->
<script> <script>
var __bs_data__ = { blessing.extra = {
rule: "@lang('user.player.player-name-rule.'.option('player_name_rule'))", rule: "@lang('user.player.player-name-rule.'.option('player_name_rule'))",
length: "@lang('user.player.player-name-length', ['min' => option('player_name_length_min'), 'max' => option('player_name_length_max')])" length: "@lang('user.player.player-name-length', ['min' => option('player_name_length_min'), 'max' => option('player_name_length_max')])"
} }

View File

@ -18,12 +18,12 @@
</div><!-- /.content-wrapper --> </div><!-- /.content-wrapper -->
<script> <script>
Object.defineProperty(window, '__bs_data__', { Object.defineProperty(blessing, 'extra', {
value: Object.freeze({ configurable: false,
get: () => Object.freeze({
admin: {{ (string) $user->isAdmin() ?: 'false' }}, admin: {{ (string) $user->isAdmin() ?: 'false' }},
unverified: {{ option('require_verification') && !$user->verified ? 'true' : 'false' }} unverified: {{ option('require_verification') && !$user->verified ? 'true' : 'false' }}
}), }),
writable: false
}) })
</script> </script>
@endsection @endsection