diff --git a/app/Http/Controllers/ClosetController.php b/app/Http/Controllers/ClosetController.php
index 03f5f953..4450d858 100644
--- a/app/Http/Controllers/ClosetController.php
+++ b/app/Http/Controllers/ClosetController.php
@@ -14,7 +14,14 @@ class ClosetController extends Controller
public function index()
{
return view('user.closet')
- ->with('extra', ['unverified' => option('require_verification') && ! $user->verified]);
+ ->with('extra', [
+ 'unverified' => option('require_verification') && ! $user->verified,
+ 'rule' => trans('user.player.player-name-rule.'.option('player_name_rule')),
+ 'length' => trans(
+ 'user.player.player-name-length',
+ ['min' => option('player_name_length_min'), 'max' => option('player_name_length_max')]
+ ),
+ ]);
}
public function getClosetData(Request $request)
diff --git a/resources/assets/src/components/AddPlayerDialog.vue b/resources/assets/src/components/AddPlayerDialog.vue
new file mode 100644
index 00000000..c5ece55b
--- /dev/null
+++ b/resources/assets/src/components/AddPlayerDialog.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+ - {{ rule }}
+ - {{ length }}
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/src/components/ApplyToPlayerDialog.vue b/resources/assets/src/components/ApplyToPlayerDialog.vue
new file mode 100644
index 00000000..dda9c7c8
--- /dev/null
+++ b/resources/assets/src/components/ApplyToPlayerDialog.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
diff --git a/resources/assets/src/views/skinlib/Show.vue b/resources/assets/src/views/skinlib/Show.vue
index 67208280..59123212 100644
--- a/resources/assets/src/views/skinlib/Show.vue
+++ b/resources/assets/src/views/skinlib/Show.vue
@@ -11,14 +11,16 @@
{{ $t('skinlib.addToCloset') }}
-
{{ $t('skinlib.apply') }}
-
+
+
+
@@ -157,10 +166,12 @@
import setAsAvatar from '../../components/mixins/setAsAvatar'
import addClosetItem from '../../components/mixins/addClosetItem'
import removeClosetItem from '../../components/mixins/removeClosetItem'
+import ApplyToPlayerDialog from '../../components/ApplyToPlayerDialog.vue'
export default {
name: 'Show',
components: {
+ ApplyToPlayerDialog,
Previewer: () => import('../../components/Previewer.vue'),
},
mixins: [
@@ -366,6 +377,9 @@ export default {
this.$message.warning(msg)
}
},
+ fetchPlayersList() {
+ this.$refs.useAs.fetchList()
+ },
},
}
diff --git a/resources/assets/src/views/user/Closet.vue b/resources/assets/src/views/user/Closet.vue
index 176c909b..86d905c2 100644
--- a/resources/assets/src/views/user/Closet.vue
+++ b/resources/assets/src/views/user/Closet.vue
@@ -131,7 +131,7 @@
type="primary"
data-toggle="modal"
data-target="#modal-use-as"
- @click="applyTexture"
+ @click="fetchPlayersList"
>
{{ $t('user.useAs') }}
@@ -142,52 +142,8 @@
-
-
+
+
@@ -196,6 +152,8 @@ import Paginate from 'vuejs-paginate'
import { debounce, queryString } from '../../scripts/utils'
import ClosetItem from '../../components/ClosetItem.vue'
import EmailVerification from '../../components/EmailVerification.vue'
+import AddPlayerDialog from '../../components/AddPlayerDialog.vue'
+import ApplyToPlayerDialog from '../../components/ApplyToPlayerDialog.vue'
export default {
name: 'Closet',
@@ -204,6 +162,8 @@ export default {
ClosetItem,
Previewer: () => import('../../components/Previewer.vue'),
EmailVerification,
+ AddPlayerDialog,
+ ApplyToPlayerDialog,
},
data: () => ({
category: 'skin',
@@ -218,8 +178,6 @@ export default {
skinUrl: '',
selectedCape: 0,
capeUrl: '',
- players: [],
- selectedPlayer: 0,
linkToSkin: `${blessing.base_url}/skinlib?filter=skin`,
linkToCape: `${blessing.base_url}/skinlib?filter=cape`,
}),
@@ -233,7 +191,7 @@ export default {
const tid = +queryString('tid', 0)
if (tid) {
this.selectTexture(tid)
- this.applyTexture()
+ this.fetchPlayersList()
$('#modal-use-as').modal()
}
},
@@ -267,9 +225,6 @@ export default {
pageChanged(page) {
this.loadCloset(page)
},
- avatarUrl(player) {
- return `${blessing.base_url}/avatar/35/${player.tid_skin}`
- },
async selectTexture(tid) {
const { type, hash } = await this.$http.get(`/skinlib/info/${tid}`)
if (type === 'cape') {
@@ -280,41 +235,15 @@ export default {
this.selectedSkin = tid
}
},
- async applyTexture() {
- this.players = await this.$http.get('/user/player/list')
- },
- async submitApplyTexture() {
- if (!this.selectedPlayer) {
- return this.$message.info(this.$t('user.emptySelectedPlayer'))
- }
-
- if (!this.selectedSkin && !this.selectedCape) {
- return this.$message.info(this.$t('user.emptySelectedTexture'))
- }
-
- const { errno, msg } = await this.$http.post(
- '/user/player/set',
- {
- pid: this.selectedPlayer,
- tid: {
- skin: this.selectedSkin || undefined,
- cape: this.selectedCape || undefined,
- },
- }
- )
- if (errno === 0) {
- this.$message.success(msg)
- $('#modal-use-as').modal('hide')
- } else {
- this.$message.warning(msg)
- }
- },
resetSelected() {
this.selectedSkin = 0
this.selectedCape = 0
this.skinUrl = ''
this.capeUrl = ''
},
+ fetchPlayersList() {
+ this.$refs.useAs.fetchList()
+ },
},
}
@@ -345,9 +274,6 @@ export default {
a.selected
color #3c8dbc
-.player-item:not(:nth-child(1))
- margin-top 10px
-
.breadcrumb
a
margin-right 10px
diff --git a/resources/assets/src/views/user/Players.vue b/resources/assets/src/views/user/Players.vue
index b388729a..da68c6cf 100644
--- a/resources/assets/src/views/user/Players.vue
+++ b/resources/assets/src/views/user/Players.vue
@@ -116,57 +116,7 @@
-
-
-
-
-
-
-
-
-
- - {{ playerNameRule }}
- - {{ playerNameLength }}
-
-
-
-
-
-
-
+
@@ -363,9 +300,6 @@ export default {
.player-selected
background-color #f5f5f5
-.modal-body > label
- margin-bottom 10px
-
.skin2d
float right
max-height 64px
diff --git a/resources/assets/tests/components/AddPlayerDialog.test.ts b/resources/assets/tests/components/AddPlayerDialog.test.ts
new file mode 100644
index 00000000..4e58807d
--- /dev/null
+++ b/resources/assets/tests/components/AddPlayerDialog.test.ts
@@ -0,0 +1,35 @@
+import Vue from 'vue'
+import { mount } from '@vue/test-utils'
+import { flushPromises } from '../utils'
+import AddPlayerDialog from '@/components/AddPlayerDialog.vue'
+
+window.blessing.extra = {
+ rule: 'rule',
+ length: 'length',
+}
+
+test('add player', async () => {
+ window.$ = jest.fn(() => ({ modal() {} }))
+ Vue.prototype.$http.get.mockResolvedValueOnce([])
+ Vue.prototype.$http.post
+ .mockResolvedValueOnce({ errno: 1, msg: 'fail' })
+ .mockResolvedValue({ errno: 0, msg: 'ok' })
+ const wrapper = mount(AddPlayerDialog)
+ const button = wrapper.find('[data-test=addPlayer]')
+ wrapper.find('input[type="text"]').setValue('the-new')
+
+ button.trigger('click')
+ expect(Vue.prototype.$http.post).toBeCalledWith(
+ '/user/player/add',
+ { player_name: 'the-new' }
+ )
+ await flushPromises()
+ await wrapper.vm.$nextTick()
+ expect(wrapper.text()).not.toContain('the-new')
+ expect(Vue.prototype.$message.warning).toBeCalledWith('fail')
+
+ button.trigger('click')
+ await flushPromises()
+ expect(wrapper.emitted().add).toBeDefined()
+ expect(Vue.prototype.$message.success).toBeCalledWith('ok')
+})
diff --git a/resources/assets/tests/components/ApplyToPlayerDialog.test.ts b/resources/assets/tests/components/ApplyToPlayerDialog.test.ts
new file mode 100644
index 00000000..7f3d9a37
--- /dev/null
+++ b/resources/assets/tests/components/ApplyToPlayerDialog.test.ts
@@ -0,0 +1,54 @@
+import Vue from 'vue'
+import { mount } from '@vue/test-utils'
+import ApplyToPlayerDialog from '@/components/ApplyToPlayerDialog.vue'
+
+test('submit applying texture', async () => {
+ window.$ = jest.fn(() => ({ modal() {} }))
+ Vue.prototype.$http.get.mockResolvedValue([{ pid: 1 }])
+ Vue.prototype.$http.post.mockResolvedValueOnce({ errno: 1 })
+ .mockResolvedValue({ errno: 0, msg: 'ok' })
+ const wrapper = mount(ApplyToPlayerDialog)
+ const button = wrapper.find('[data-test=submit]')
+
+ button.trigger('click')
+ expect(Vue.prototype.$message.info).toBeCalledWith('user.emptySelectedPlayer')
+
+ wrapper.setData({ selected: 1 })
+ button.trigger('click')
+ expect(Vue.prototype.$message.info).toBeCalledWith('user.emptySelectedTexture')
+
+ wrapper.setProps({ skin: 1 })
+ button.trigger('click')
+ expect(Vue.prototype.$http.post).toBeCalledWith(
+ '/user/player/set',
+ {
+ pid: 1,
+ tid: {
+ skin: 1,
+ cape: undefined,
+ },
+ }
+ )
+ wrapper.setProps({ skin: 0, cape: 1 })
+ button.trigger('click')
+ expect(Vue.prototype.$http.post).toBeCalledWith(
+ '/user/player/set',
+ {
+ pid: 1,
+ tid: {
+ skin: undefined,
+ cape: 1,
+ },
+ }
+ )
+ await wrapper.vm.$nextTick()
+ expect(Vue.prototype.$message.success).toBeCalledWith('ok')
+})
+
+test('compute avatar URL', () => {
+ Vue.prototype.$http.get.mockResolvedValue({})
+ // eslint-disable-next-line camelcase
+ const wrapper = mount(ApplyToPlayerDialog)
+ const { avatarUrl } = wrapper.vm
+ expect(avatarUrl({ tid_skin: 1 })).toBe('/avatar/35/1')
+})
diff --git a/resources/assets/tests/views/skinlib/Show.test.ts b/resources/assets/tests/views/skinlib/Show.test.ts
index 0a4de65a..afa71df0 100644
--- a/resources/assets/tests/views/skinlib/Show.test.ts
+++ b/resources/assets/tests/views/skinlib/Show.test.ts
@@ -419,3 +419,17 @@ test('report texture', async () => {
await flushPromises()
expect(Vue.prototype.$message.success).toBeCalledWith('success')
})
+
+test('apply texture to player', () => {
+ Vue.prototype.$http.get
+ .mockResolvedValue({})
+ .mockResolvedValue([])
+ const wrapper = mount(Show, {
+ mocks: {
+ $route: ['/skinlib/show/1', '1'],
+ },
+ stubs: { previewer },
+ })
+ wrapper.find('[data-target="#modal-use-as"]').trigger('click')
+ expect(wrapper.find('[data-target="#modal-add-player"]').exists()).toBeFalse()
+})
diff --git a/resources/assets/tests/views/user/Closet.test.ts b/resources/assets/tests/views/user/Closet.test.ts
index 0cb4c08a..86665097 100644
--- a/resources/assets/tests/views/user/Closet.test.ts
+++ b/resources/assets/tests/views/user/Closet.test.ts
@@ -145,14 +145,6 @@ test('remove cape item', () => {
expect(wrapper.find('#cape-category').text()).toContain('user.emptyClosetMsg')
})
-test('compute avatar URL', () => {
- Vue.prototype.$http.get.mockResolvedValue({})
- // eslint-disable-next-line camelcase
- const wrapper = mount(Closet)
- const { avatarUrl } = wrapper.vm
- expect(avatarUrl({ tid_skin: 1 })).toBe('/avatar/35/1')
-})
-
test('select texture', async () => {
Vue.prototype.$http.get
.mockResolvedValueOnce({})
@@ -200,49 +192,6 @@ test('apply texture', async () => {
jest.runAllTimers()
})
-test('submit applying texture', async () => {
- window.$ = jest.fn(() => ({ modal() {} }))
- Vue.prototype.$http.get.mockResolvedValue({})
- Vue.prototype.$http.post.mockResolvedValueOnce({ errno: 1 })
- .mockResolvedValue({ errno: 0, msg: 'ok' })
- const wrapper = mount(Closet)
- const button = wrapper.find('[data-test=submitApplyTexture]')
-
- button.trigger('click')
- expect(Vue.prototype.$message.info).toBeCalledWith('user.emptySelectedPlayer')
-
- wrapper.setData({ selectedPlayer: 1 })
- button.trigger('click')
- expect(Vue.prototype.$message.info).toBeCalledWith('user.emptySelectedTexture')
-
- wrapper.setData({ selectedSkin: 1 })
- button.trigger('click')
- expect(Vue.prototype.$http.post).toBeCalledWith(
- '/user/player/set',
- {
- pid: 1,
- tid: {
- skin: 1,
- cape: undefined,
- },
- }
- )
- wrapper.setData({ selectedSkin: 0, selectedCape: 1 })
- button.trigger('click')
- expect(Vue.prototype.$http.post).toBeCalledWith(
- '/user/player/set',
- {
- pid: 1,
- tid: {
- skin: undefined,
- cape: 1,
- },
- }
- )
- await wrapper.vm.$nextTick()
- expect(Vue.prototype.$message.success).toBeCalledWith('ok')
-})
-
test('reset selected texture', () => {
Vue.prototype.$http.get.mockResolvedValue({})
const wrapper = mount(Closet)
diff --git a/resources/assets/tests/views/user/Players.test.ts b/resources/assets/tests/views/user/Players.test.ts
index 16a4231b..fc6e0bd6 100644
--- a/resources/assets/tests/views/user/Players.test.ts
+++ b/resources/assets/tests/views/user/Players.test.ts
@@ -127,22 +127,11 @@ test('toggle preview mode', () => {
test('add player', async () => {
window.$ = jest.fn(() => ({ modal() {} }))
Vue.prototype.$http.get.mockResolvedValueOnce([])
- Vue.prototype.$http.post
- .mockResolvedValueOnce({ errno: 1 })
- .mockResolvedValue({ errno: 0 })
+ Vue.prototype.$http.post.mockResolvedValue({ errno: 0 })
const wrapper = mount(Players)
const button = wrapper.find('[data-test=addPlayer]')
+
wrapper.find('input[type="text"]').setValue('the-new')
-
- button.trigger('click')
- expect(Vue.prototype.$http.post).toBeCalledWith(
- '/user/player/add',
- { player_name: 'the-new' }
- )
- await flushPromises()
- await wrapper.vm.$nextTick()
- expect(wrapper.text()).not.toContain('the-new')
-
button.trigger('click')
await flushPromises()
expect(Vue.prototype.$http.get).toBeCalledTimes(2)