Add skin library "show" page
This commit is contained in:
parent
d39f95d634
commit
deda5cf11c
|
|
@ -59,6 +59,11 @@ export default [
|
|||
component: () => import('./skinlib/List'),
|
||||
el: '.content-wrapper'
|
||||
},
|
||||
{
|
||||
path: 'skinlib/show/(\\d+)',
|
||||
component: () => import('./skinlib/Show'),
|
||||
el: '.content > .row:nth-child(1)'
|
||||
},
|
||||
{
|
||||
path: 'skinlib/upload',
|
||||
component: () => import('./skinlib/Upload'),
|
||||
|
|
|
|||
308
resources/assets/src/components/skinlib/Show.vue
Normal file
308
resources/assets/src/components/skinlib/Show.vue
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<previewer
|
||||
:skin="type !== 'cape' && textureUrl"
|
||||
:cape="type === 'cape' ? textureUrl : ''"
|
||||
:init-position-z="60"
|
||||
>
|
||||
<template slot="footer">
|
||||
<button
|
||||
v-if="!auth"
|
||||
disabled
|
||||
:title="$t('skinlib.show.anonymous')"
|
||||
class="btn btn-primary pull-right"
|
||||
v-t="'skinlib.addToCloset'"
|
||||
></button>
|
||||
<template v-else>
|
||||
<a
|
||||
v-if="liked"
|
||||
@click="removeFromCloset"
|
||||
class="btn btn-primary pull-right"
|
||||
v-t="'skinlib.removeFromCloset'"
|
||||
></a>
|
||||
<a
|
||||
v-else
|
||||
@click="addToCloset"
|
||||
class="btn btn-primary pull-right"
|
||||
v-t="'skinlib.addToCloset'"
|
||||
></a>
|
||||
</template>
|
||||
<div
|
||||
class="btn likes"
|
||||
style="cursor: auto;"
|
||||
:style="{ color: liked ? '#e0353b' : '#333' }"
|
||||
:title="$t('skinlib.show.likes')"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
>
|
||||
<i class="fas fa-heart"></i>
|
||||
<span>{{ likes }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</previewer>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title" v-t="'skinlib.show.detail'"></h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td v-t="'skinlib.show.name'"></td>
|
||||
<td>{{ name }}
|
||||
<small v-if="uploader === currentUid || admin">
|
||||
<a href="#" @click="changeTextureName" v-t="'skinlib.show.edit-name'"></a>
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-t="'skinlib.show.model'"></td>
|
||||
<td>
|
||||
<template v-if="type === 'cape'">{{ $t('general.cape') }}</template>
|
||||
<template v-else>{{ type }}</template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TID</td>
|
||||
<td>{{ tid }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hash
|
||||
<i
|
||||
v-if="canBeDownloaded"
|
||||
class="fas fa-question-circle"
|
||||
:title="$t('skinlib.show.download-raw')"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
></i>
|
||||
</td>
|
||||
<td>
|
||||
<a v-if="canBeDownloaded" :href="`${baseUrl}/raw/${tid}.png`" :title="hash">{{ hash.slice(0, 15) }}...</a>
|
||||
<span v-else :title="hash">{{ hash.slice(0, 15) }}...</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-t="'skinlib.show.size'"></td>
|
||||
<td>{{ size }} KB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-t="'skinlib.show.uploader'"></td>
|
||||
<template v-if="uploaderNickName != null">
|
||||
<td><a :href="`${baseUrl}/skinlib?filter=${type === 'cape' ? 'cape' : 'skin'}&uploader=${uploader}`">{{ uploaderNickName }}</a></td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td><span v-t="'general.unexistent-user'"></span></td>
|
||||
</template>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-t="'skinlib.show.upload-at'"></td>
|
||||
<td>{{ uploadAt }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!-- /.box-body -->
|
||||
</div><!-- /.box -->
|
||||
|
||||
<div v-if="auth" class="box box-warning">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title" v-t="'admin.operationsTitle'" />
|
||||
</div><!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<p v-t="'skinlib.show.manage-notice'"></p>
|
||||
</div><!-- /.box-body -->
|
||||
|
||||
<div class="box-footer">
|
||||
<a @click="togglePrivacy" class="btn btn-warning" v-t="togglePrivacyText"></a>
|
||||
<a @click="deleteTexture" class="btn btn-danger pull-right" v-t="'skinlib.show.delete-texture'"></a>
|
||||
</div><!-- /.box-footer -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { swal } from '../../js/notify';
|
||||
import toastr from 'toastr';
|
||||
|
||||
export default {
|
||||
name: 'Show',
|
||||
components: {
|
||||
Previewer: () => import('../common/Previewer')
|
||||
},
|
||||
props: {
|
||||
baseUrl: {
|
||||
default: blessing.base_url
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tid: +this.$route[1],
|
||||
name: '',
|
||||
type: 'steve',
|
||||
likes: 0,
|
||||
hash: '',
|
||||
uploader: 0,
|
||||
size: 0,
|
||||
uploadAt: '',
|
||||
public: true,
|
||||
liked: __bs_data__.inCloset,
|
||||
canBeDownloaded: __bs_data__.download,
|
||||
currentUid: __bs_data__.currentUid,
|
||||
admin: __bs_data__.admin,
|
||||
uploaderNickName: __bs_data__.nickname,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
auth() {
|
||||
return !!this.currentUid;
|
||||
},
|
||||
togglePrivacyText() {
|
||||
return this.public ? 'skinlib.setAsPrivate' : 'skinlib.setAsPublic';
|
||||
},
|
||||
textureUrl() {
|
||||
return `${this.baseUrl}/textures/${this.hash}`;
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
const data = await this.$http.get(`/skinlib/info/${this.tid}`);
|
||||
this.name = data.name;
|
||||
this.type = data.type;
|
||||
this.likes = data.likes;
|
||||
this.hash = data.hash;
|
||||
this.uploader = data.uploader;
|
||||
this.size = data.size;
|
||||
this.uploadAt = data.upload_at;
|
||||
this.public = !!data.public;
|
||||
},
|
||||
async addToCloset() {
|
||||
const { dismiss, value } = await swal({
|
||||
title: this.$t('skinlib.setItemName'),
|
||||
inputValue: this.name,
|
||||
input: 'text',
|
||||
showCancelButton: true,
|
||||
inputValidator: value => !value && this.$t('skinlib.emptyItemName')
|
||||
});
|
||||
if (dismiss) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { errno, msg } = await this.$http.post(
|
||||
'/user/closet/add',
|
||||
{ tid: this.tid, name: value }
|
||||
);
|
||||
if (errno === 0) {
|
||||
this.liked = true;
|
||||
this.likes++;
|
||||
swal({ type: 'success', text: msg });
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
},
|
||||
async removeFromCloset() {
|
||||
const { dismiss } = await swal({
|
||||
text: this.$t('user.removeFromClosetNotice'),
|
||||
type: 'warning',
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: '#3085d6',
|
||||
confirmButtonColor: '#d33'
|
||||
});
|
||||
if (dismiss) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { errno, msg } = await this.$http.post(
|
||||
'/user/closet/remove',
|
||||
{ tid: this.tid }
|
||||
);
|
||||
if (errno === 0) {
|
||||
this.liked = false;
|
||||
this.likes--;
|
||||
swal({ type: 'success', text: msg });
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
},
|
||||
async changeTextureName() {
|
||||
const { dismiss, value } = await swal({
|
||||
text: this.$t('skinlib.setNewTextureName'),
|
||||
input: 'text',
|
||||
inputValue: this.name,
|
||||
showCancelButton: true,
|
||||
inputValidator: name => !name && this.$t('skinlib.emptyNewTextureName')
|
||||
});
|
||||
if (dismiss) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { errno, msg } = await this.$http.post(
|
||||
'/skinlib/rename',
|
||||
{ tid: this.tid, new_name: value }
|
||||
);
|
||||
if (errno === 0) {
|
||||
this.name = value;
|
||||
toastr.success(msg);
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
},
|
||||
async togglePrivacy() {
|
||||
const { dismiss } = await swal({
|
||||
text: this.public
|
||||
? this.$t('skinlib.setPrivateNotice')
|
||||
: this.$t('skinlib.setPublicNotice'),
|
||||
type: 'warning',
|
||||
showCancelButton: true
|
||||
});
|
||||
if (dismiss) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { errno, msg } = await this.$http.post(
|
||||
'/skinlib/privacy',
|
||||
{ tid: this.tid }
|
||||
);
|
||||
if (errno === 0) {
|
||||
toastr.success(msg);
|
||||
this.public = !this.public;
|
||||
} else {
|
||||
toastr.warning(msg);
|
||||
}
|
||||
},
|
||||
async deleteTexture() {
|
||||
const { dismiss } = await swal({
|
||||
text: this.$t('skinlib.deleteNotice'),
|
||||
type: 'warning',
|
||||
showCancelButton: true
|
||||
});
|
||||
if (dismiss) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { errno, msg } = await this.$http.post(
|
||||
'/skinlib/delete',
|
||||
{ tid: this.tid }
|
||||
);
|
||||
if (errno === 0) {
|
||||
await swal({ type: 'success', text: msg });
|
||||
window.location = `${this.baseUrl}/skinlib`;
|
||||
} else {
|
||||
swal({ type: 'warning', text: msg });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.table > tbody > tr > td {
|
||||
border-top: 0;
|
||||
}
|
||||
</style>
|
||||
317
resources/assets/tests/components/skinlib/Show.test.js
Normal file
317
resources/assets/tests/components/skinlib/Show.test.js
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
import Vue from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import Show from '@/components/skinlib/Show';
|
||||
import { flushPromises } from '../../utils';
|
||||
import { swal } from '@/js/notify';
|
||||
import toastr from 'toastr';
|
||||
|
||||
jest.mock('@/js/notify');
|
||||
|
||||
window.__bs_data__ = {
|
||||
download: true,
|
||||
currentUid: 0,
|
||||
admin: false,
|
||||
nickname: 'author',
|
||||
inCloset: false,
|
||||
};
|
||||
|
||||
/** @type {import('Vue').ComponentOptions} */
|
||||
const previewer = {
|
||||
render(h) {
|
||||
return h('div', this.$slots.footer);
|
||||
},
|
||||
};
|
||||
|
||||
test('button for adding to closet should be disabled if not auth', () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue({});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
expect(wrapper.find('.btn-primary').attributes()).toHaveProperty('disabled', 'disabled');
|
||||
});
|
||||
|
||||
test('button for adding to closet should be disabled if auth', () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue({});
|
||||
Object.assign(window.__bs_data__, { inCloset: true, currentUid: 1 });
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
expect(wrapper.find('.btn-primary').text()).toBe('skinlib.removeFromCloset');
|
||||
});
|
||||
|
||||
test('likes count indicator', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue({ likes: 2 });
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.find('.likes').attributes().style).toContain('color: rgb(224, 53, 59)');
|
||||
expect(wrapper.find('.likes').text()).toContain('2');
|
||||
});
|
||||
|
||||
test('render basic information', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue({
|
||||
name: 'my-texture',
|
||||
type: 'alex',
|
||||
hash: '123',
|
||||
size: 2,
|
||||
upload_at: '2018'
|
||||
});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
}
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const text = wrapper.find('.box-primary').text();
|
||||
expect(text).toContain('my-texture');
|
||||
expect(text).toContain('alex');
|
||||
expect(text).toContain('123...');
|
||||
expect(text).toContain('2 KB');
|
||||
expect(text).toContain('2018');
|
||||
expect(text).toContain('author');
|
||||
});
|
||||
|
||||
test('render action text of editing texture name', async () => {
|
||||
Object.assign(window.__bs_data__, { admin: true });
|
||||
Vue.prototype.$http.get.mockResolvedValue({ uploader: 1, name: 'name' });
|
||||
|
||||
let wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
}
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.contains('small')).toBeTrue();
|
||||
|
||||
Object.assign(window.__bs_data__, { currentUid: 2, admin: false });
|
||||
wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
}
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.contains('small')).toBeFalse();
|
||||
});
|
||||
|
||||
test('render nickname of uploader', () => {
|
||||
Object.assign(window.__bs_data__, { nickname: null });
|
||||
Vue.prototype.$http.get.mockResolvedValue({});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
}
|
||||
});
|
||||
expect(wrapper.text()).toContain('general.unexistent-user');
|
||||
});
|
||||
|
||||
test('operation panel should not be rendered if not auth', () => {
|
||||
Object.assign(window.__bs_data__, { currentUid: 0 });
|
||||
Vue.prototype.$http.get.mockResolvedValue({});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
}
|
||||
});
|
||||
expect(wrapper.contains('.box-warning')).toBeFalse();
|
||||
});
|
||||
|
||||
test('link to downloading texture', async () => {
|
||||
Object.assign(window.__bs_data__, { download: false });
|
||||
Vue.prototype.$http.get.mockResolvedValue({ hash: '123' });
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
}
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.contains('a[title="123"]')).toBeFalse();
|
||||
expect(wrapper.contains('span[title="123"]')).toBeTrue();
|
||||
});
|
||||
|
||||
test('add to closet', async () => {
|
||||
Object.assign(window.__bs_data__, { currentUid: 1, inCloset: false });
|
||||
Vue.prototype.$http.get.mockResolvedValue({ name: 'wow', likes: 2 });
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ errno: 1, msg: '1' })
|
||||
.mockResolvedValue({ errno: 0, msg: '' });
|
||||
jest.spyOn(toastr, 'warning');
|
||||
swal.mockImplementationOnce(() => ({ dismiss: 1 }))
|
||||
.mockImplementation(({ inputValidator }) => {
|
||||
if (inputValidator) {
|
||||
inputValidator();
|
||||
inputValidator('wow');
|
||||
}
|
||||
return { value: 'wow' };
|
||||
});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
const button = wrapper.find('.btn-primary');
|
||||
|
||||
button.trigger('click');
|
||||
expect(Vue.prototype.$http.post).not.toBeCalled();
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/user/closet/add',
|
||||
{ tid: 1, name: 'wow' }
|
||||
);
|
||||
expect(toastr.warning).toBeCalledWith('1');
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.likes).toBe(3);
|
||||
expect(wrapper.vm.liked).toBeTrue();
|
||||
});
|
||||
|
||||
test('remove from closet', async () => {
|
||||
Object.assign(window.__bs_data__, { currentUid: 1, inCloset: true });
|
||||
Vue.prototype.$http.get.mockResolvedValue({ likes: 2 });
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ errno: 1, msg: '1' })
|
||||
.mockResolvedValue({ errno: 0, msg: '' });
|
||||
jest.spyOn(toastr, 'warning');
|
||||
swal.mockResolvedValueOnce({ dismiss: 1 })
|
||||
.mockResolvedValue({});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
const button = wrapper.find('.btn-primary');
|
||||
|
||||
button.trigger('click');
|
||||
expect(Vue.prototype.$http.post).not.toBeCalled();
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/user/closet/remove',
|
||||
{ tid: 1 }
|
||||
);
|
||||
expect(toastr.warning).toBeCalledWith('1');
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.likes).toBe(1);
|
||||
expect(wrapper.vm.liked).toBeFalse();
|
||||
});
|
||||
|
||||
test('change texture name', async () => {
|
||||
Object.assign(window.__bs_data__, { admin: true });
|
||||
Vue.prototype.$http.get.mockResolvedValue({ name: 'old-name' });
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ errno: 1, msg: '1' })
|
||||
.mockResolvedValue({ errno: 0, msg: '0' });
|
||||
jest.spyOn(toastr, 'warning');
|
||||
swal.mockImplementationOnce(() => ({ dismiss: 1 }))
|
||||
.mockImplementation(({ inputValidator }) => {
|
||||
inputValidator();
|
||||
inputValidator('new-name');
|
||||
return { value: 'new-name' };
|
||||
});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
const button = wrapper.find('small > a');
|
||||
|
||||
button.trigger('click');
|
||||
expect(Vue.prototype.$http.post).not.toBeCalled();
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/skinlib/rename',
|
||||
{ tid: 1, new_name: 'new-name' }
|
||||
);
|
||||
expect(toastr.warning).toBeCalledWith('1');
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.name).toBe('new-name');
|
||||
});
|
||||
|
||||
test('toggle privacy', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue({ public: true });
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ errno: 1, msg: '1' })
|
||||
.mockResolvedValue({ errno: 0, msg: '0' });
|
||||
jest.spyOn(toastr, 'warning');
|
||||
swal.mockResolvedValueOnce({ dismiss: 1 })
|
||||
.mockResolvedValue({});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
const button = wrapper.find('.btn-warning');
|
||||
|
||||
button.trigger('click');
|
||||
expect(Vue.prototype.$http.post).not.toBeCalled();
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/skinlib/privacy',
|
||||
{ tid: 1 }
|
||||
);
|
||||
expect(toastr.warning).toBeCalledWith('1');
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.public).toBeFalse();
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(wrapper.vm.public).toBeTrue();
|
||||
});
|
||||
|
||||
test('delete texture', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue({});
|
||||
Vue.prototype.$http.post
|
||||
.mockResolvedValueOnce({ errno: 1, msg: '1' })
|
||||
.mockResolvedValue({ errno: 0, msg: '0' });
|
||||
swal.mockResolvedValueOnce({ dismiss: 1 })
|
||||
.mockResolvedValue({});
|
||||
const wrapper = mount(Show, {
|
||||
mocks: {
|
||||
$route: ['/skinlib/show/1', '1']
|
||||
},
|
||||
stubs: { previewer }
|
||||
});
|
||||
const button = wrapper.find('.btn-danger');
|
||||
|
||||
button.trigger('click');
|
||||
expect(Vue.prototype.$http.post).not.toBeCalled();
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(Vue.prototype.$http.post).toBeCalledWith(
|
||||
'/skinlib/delete',
|
||||
{ tid: 1 }
|
||||
);
|
||||
expect(swal).toBeCalledWith({ type: 'warning', text: '1' });
|
||||
|
||||
button.trigger('click');
|
||||
await flushPromises();
|
||||
expect(swal).toBeCalledWith({ type: 'success', text: '0' });
|
||||
});
|
||||
|
|
@ -68,6 +68,7 @@ skinlib:
|
|||
setAsPrivate: Set as Private
|
||||
setAsPublic: Set as Public
|
||||
setPublicNotice: Sure to set this as public texture?
|
||||
setPrivateNotice: Sure to set this as private texture?
|
||||
deleteNotice: Are you sure to delete this texture?
|
||||
upload:
|
||||
texture-name: Texture Name
|
||||
|
|
@ -79,6 +80,19 @@ skinlib:
|
|||
dropZone: Drop a file here
|
||||
remove: Remove
|
||||
cost: (It cost you about :score score)
|
||||
show:
|
||||
anonymous: You must login to use closets
|
||||
likes: People who like this
|
||||
detail: Details
|
||||
name: Texture Name
|
||||
edit-name: Edit Name
|
||||
model: Applicable Model
|
||||
download-raw: Click to download raw texture
|
||||
size: File Size
|
||||
uploader: Uploader
|
||||
upload-at: Upload At
|
||||
delete-texture: Delete Texture
|
||||
notice: The texture which was deleted or setted to private will be removed from the closet of everyone who had favorited it.
|
||||
|
||||
user:
|
||||
signRemainingTime: 'Available after :time :unit'
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ skinlib:
|
|||
setAsPrivate: 设为隐私
|
||||
setAsPublic: 设为公开
|
||||
setPublicNotice: 要将此材质设置为公开吗?
|
||||
setPrivateNotice: 要将此材质设置为私有吗?
|
||||
deleteNotice: 真的要删除此材质吗?
|
||||
upload:
|
||||
texture-name: 材质名称
|
||||
|
|
@ -81,6 +82,19 @@ skinlib:
|
|||
dropZone: 拖拽文件到这里
|
||||
remove: 移除
|
||||
cost: (这会消耗您约 :score 积分)
|
||||
show:
|
||||
anonymous: 登录后才能使用衣柜哦
|
||||
likes: 收藏人数
|
||||
detail: 详细信息
|
||||
name: 名称
|
||||
edit-name: 修改名称
|
||||
model: 适用模型
|
||||
download-raw: 右键另存为即可下载原始皮肤文件
|
||||
size: 文件大小
|
||||
uploader: 上传者
|
||||
upload-at: 上传日期
|
||||
delete-texture: 删除材质
|
||||
manage-notice: 材质设为隐私或被删除后将会从每一个收藏者的衣柜中移除。
|
||||
|
||||
user:
|
||||
signRemainingTime: ':time :unit 后可签到'
|
||||
|
|
|
|||
|
|
@ -15,108 +15,7 @@
|
|||
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="box box-primary">
|
||||
@include('common.texture-preview')
|
||||
|
||||
<div class="box-footer">
|
||||
@if (is_null($user)) {{-- Not logged in --}}
|
||||
<button disabled="disabled" title="@lang('skinlib.show.anonymous')" class="btn btn-primary pull-right">@lang('skinlib.item.add-to-closet')</button>
|
||||
@else
|
||||
|
||||
@if ($user->getCloset()->has($texture->tid))
|
||||
<a onclick="removeFromCloset({{ $texture->tid }});" id="{{ $texture->tid }}" class="btn btn-primary pull-right">@lang('skinlib.item.remove-from-closet')</a>
|
||||
@else
|
||||
<a onclick="addToCloset({{ $texture->tid }});" id="{{ $texture->tid }}" class="btn btn-primary pull-right">@lang('skinlib.item.add-to-closet')</a>
|
||||
@endif
|
||||
|
||||
@endif
|
||||
<div class="btn likes" title="@lang('skinlib.show.likes')" data-toggle="tooltip" data-placement="top"><i class="fas fa-heart"></i>
|
||||
<span id="likes">{{ $texture->likes }}</span>
|
||||
</div>
|
||||
</div><!-- /.box-footer -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">@lang('skinlib.show.detail')</h3>
|
||||
</div><!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>@lang('skinlib.show.name')</td>
|
||||
<td id="name">{{ $texture->name }}
|
||||
@if (!is_null($user) && ($texture->uploader == $user->uid || $user->isAdmin()))
|
||||
<small>
|
||||
<a style="cursor: pointer" onclick="changeTextureName({{ $texture->tid }}, '{{ $texture->name }}');">@lang('skinlib.show.edit-name')</a>
|
||||
</small>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('skinlib.show.model')</td>
|
||||
<td>
|
||||
@if ($texture->type == 'cape')
|
||||
@lang('general.cape')
|
||||
@else
|
||||
{{ $texture->type }}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hash
|
||||
@if (option('allow_downloading_texture'))
|
||||
<i class="fas fa-question-circle" title="@lang('skinlib.show.download-raw')" data-toggle="tooltip" data-placement="top"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if (option('allow_downloading_texture'))
|
||||
<a href="{{ url('raw/'.$texture->tid) }}.png" title="{{ $texture->hash }}">{{ substr($texture->hash, 0, 15) }}...</a>
|
||||
@else
|
||||
<span title="{{ $texture->hash }}">{{ substr($texture->hash, 0, 15) }}...</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('skinlib.show.size')</td>
|
||||
<td>{{ $texture->size }} KB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('skinlib.show.uploader')</td>
|
||||
@if ($uploader = app('users')->get($texture->uploader))
|
||||
<td><a href="{{ url('skinlib?filter='.($texture->type == 'cape' ? 'cape' : 'skin').'&uploader='.$uploader->uid) }}&sort=time">{{ $uploader->getNickName() }}</a></td>
|
||||
@else
|
||||
<td><a href="#">@lang('general.unexistent-user')</a></td>
|
||||
@endif
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('skinlib.show.upload-at')</td>
|
||||
<td>{{ $texture->upload_at }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!-- /.box-body -->
|
||||
</div><!-- /.box -->
|
||||
|
||||
@if (!is_null($user))
|
||||
@if ($texture->uploader == $user->uid)
|
||||
@include('common.manage-panel', [
|
||||
'title' => trans('skinlib.show.delete-texture')." / ".trans('skinlib.privacy.change-privacy'),
|
||||
'message' => trans('skinlib.show.notice')
|
||||
])
|
||||
|
||||
@elseif ($user->isAdmin())
|
||||
@include('common.manage-panel', [
|
||||
'title' => trans('skinlib.show.manage-panel'),
|
||||
'message' => trans('skinlib.show.notice-admin')
|
||||
])
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="row"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
|
@ -141,25 +40,19 @@
|
|||
</div><!-- /.container -->
|
||||
</div><!-- /.content-wrapper -->
|
||||
|
||||
@endsection
|
||||
|
||||
@section('script')
|
||||
<script>
|
||||
var texture = {!! $texture->toJson() !!};
|
||||
|
||||
$.msp.config.slim = (texture.type === 'alex');
|
||||
$.msp.config.skinUrl = texture.type === 'alex' ? defaultAlexSkin : defaultSteveSkin;
|
||||
|
||||
if (texture.type === 'cape') {
|
||||
$.msp.config.capeUrl = url('textures/' + texture.hash);
|
||||
} else {
|
||||
$.msp.config.skinUrl = url('textures/' + texture.hash);
|
||||
Object.defineProperty(window, '__bs_data__', {
|
||||
configurable: false,
|
||||
get: function () {
|
||||
return Object.freeze({
|
||||
download: {{ option('allow_downloading_texture') ? 'true' : 'false' }},
|
||||
currentUid: {{ is_null($user) ? '0' : $user->uid }},
|
||||
admin: {{ $user && $user->isAdmin() ? 'true' : 'false' }},
|
||||
inCloset: {{ $user && $user->getCloset()->has($texture->tid) ? 'true' : 'false' }},
|
||||
nickname: @php echo ($up = app('users')->get($texture->uploader)) ? '"'.$up->nickname.'"' : 'null' @endphp
|
||||
})
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
initSkinViewer(60);
|
||||
registerAnimationController();
|
||||
registerWindowResizeHandler();
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user