diff --git a/app/Http/Controllers/SkinlibController.php b/app/Http/Controllers/SkinlibController.php index efa04cb1..776cf2b7 100644 --- a/app/Http/Controllers/SkinlibController.php +++ b/app/Http/Controllers/SkinlibController.php @@ -28,94 +28,91 @@ class SkinlibController extends Controller $this->user = $users->get(session('uid')); } - public function index(Request $request) + public function index() { - $filter = $request->input('filter', 'skin'); - $sort = $request->input('sort', 'time'); - $uid = intval($request->input('uid', 0)); - $page = $request->input('page', 1); - $page = $page <= 0 ? 1 : $page; - - $sort_by = ($sort == "time") ? "upload_at" : $sort; - - if ($filter == "skin") { - $textures = Texture::where(function($query) { - // Nested condition, DO NOT MODIFY - $query->where('type', '=', 'steve')->orWhere('type', '=', 'alex'); - }); - } else { - $textures = Texture::where('type', $filter); - } - - if ($uid != 0) { - $textures = $textures->where('uploader', $uid); - } - - if (!is_null($this->user)) { - - // show private textures when show uploaded textures of current user - if ($uid != $this->user->uid && !$this->user->isAdmin()) { - $textures = $textures->where('public', 1) - ->merge($textures->where('uploader', $this->user->uid)); - } - - } else { - $textures = $textures->where('public', 1); - } - - $total_pages = ceil($textures->count() / 20); - - $textures = $textures->orderBy($sort_by, 'desc')->skip(($page - 1) * 20)->take(20)->get(); - - return view('skinlib.index')->with('user', $this->user) - ->with('sort', $sort) - ->with('filter', $filter) - ->with('uploader', $uid) - ->with('textures', $textures) - ->with('page', $page) - ->with('total_pages', $total_pages); + return view('skinlib.index', ['user' => $this->user]); } - public function search(Request $request) + /** + * Get skin library data filtered. + * Available Query String: filter, uploader, page, sort, keyword, items_per_page. + * + * @param Request $request [description] + * @return [type] [description] + */ + public function getSkinlibFiltered(Request $request) { - $q = $request->input('q', ''); + + // Available filters: skin, steve, alex, cape $filter = $request->input('filter', 'skin'); - $sort = $request->input('sort', 'time'); - $sort_by = ($sort == "time") ? "upload_at" : $sort; + // Filter result by uploader's uid + $uploader = intval($request->input('uploader', 0)); - if ($q == '') { - return redirect('skinlib'); - } + // Available sorting methods: time, likes + $sort = $request->input('sort', 'time'); + $sortBy = ($sort == "time") ? "upload_at" : $sort; + + // Current page + $page = $request->input('page', 1); + $currentPage = ($page <= 0) ? 1 : $page; + + // How many items to show in one page + $itemsPerPage = $request->input('items_per_page', 20); + + // Keyword to search + $keyword = $request->input('keyword', ''); + + // Check if user logged in + $anonymous = is_null($this->user); if ($filter == "skin") { - $textures = Texture::like('name', $q)->where(function($query) use ($q) { - $query->where('type', '=', 'steve') - ->orWhere('type', '=', 'alex'); - })->orderBy($sort_by, 'desc')->get(); + $query = Texture::where(function ($innerQuery) { + // Nested condition, DO NOT MODIFY + $innerQuery->where('type', '=', 'steve')->orWhere('type', '=', 'alex'); + }); } else { - $textures = Texture::like('name', $q) - ->where('type', $filter) - ->where('public', 1) - ->orderBy($sort_by, 'desc')->get(); + $query = Texture::where('type', $filter); } - if (!is_null($this->user)) { - // show private textures when show uploaded textures of current user - if (!$this->user->isAdmin()) { - $textures = $textures->where('public', 1) - ->merge($textures->where('uploader', $this->user->uid)); + if ($keyword !== "") { + $query = $query->like('name', $keyword); + } + + if ($uploader !== 0) { + $query = $query->where('uploader', $uploader); + } + + if ($anonymous) { + // Show public textures only to anonymous visitors + $query = $query->where('public', 1); + } else { + // Show private textures when show uploaded textures of current user + if ($uploader != $this->user->uid && !$this->user->isAdmin()) { + $query = $query->where(function ($innerQuery) { + $innerQuery->where('public', '=', '1')->orWhere('uploader', '=', $this->user->uid); + }); } - } else { - $textures = $textures->where('public', 1); } - return view('skinlib.search')->with('user', $this->user) - ->with('sort', $sort) - ->with('filter', $filter) - ->with('uploader', 0) - ->with('q', $q) - ->with('textures', $textures); + $totalPages = ceil($query->count() / 20); + + $textures = $query->orderBy($sortBy, 'desc') + ->skip(($currentPage - 1) * $itemsPerPage) + ->take($itemsPerPage) + ->get(); + + if (! $anonymous) { + foreach ($textures as $item) { + $item->liked = $this->user->getCloset()->has($item->tid); + } + } + + return response()->json([ + 'items' => $textures, + 'anonymous' => $anonymous, + 'total_pages' => $totalPages + ]); } public function show($tid) diff --git a/resources/assets/src/scripts/general.js b/resources/assets/src/scripts/general.js index d30dc253..8cb2dce1 100644 --- a/resources/assets/src/scripts/general.js +++ b/resources/assets/src/scripts/general.js @@ -12,6 +12,17 @@ console.log(`\n %c Blessing Skin v${blessing.version} %c https://blessing.studio $.locales = {}; $.currentLocale = {}; +$.defaultPaginatorConfig = { + visiblePages: 5, + currentPage: 1, + first: '
  • «
  • ', + prev: '
  • ', + next: '
  • ', + last: '
  • »
  • ', + page: '
  • {{page}}
  • ', + wrapper: '' +}; + /** * Check if given value is empty. * @@ -23,6 +34,8 @@ function isEmpty(obj) { // null and undefined are "empty" if (obj == null) return true; + if (typeof (obj) == 'number' || typeof (obj) == 'boolean') return false; + // Assume if it has a length property with a non-zero value // that that property is correct. if (obj.length > 0) return false; @@ -145,13 +158,14 @@ function showAjaxError(json) { * Get parameters in query string with key. * * @param {string} key + * @param {string} defaultValue * @return {string} */ -function getQueryString(key) { +function getQueryString(key, defaultValue) { result = location.search.match(new RegExp('[\?\&]'+key+'=([^\&]+)','i')); if (result == null || result.length < 1){ - return null; + return defaultValue; } else { return result[1]; } diff --git a/resources/assets/src/scripts/skinlib.js b/resources/assets/src/scripts/skinlib.js index d5d7b6c7..be00e657 100644 --- a/resources/assets/src/scripts/skinlib.js +++ b/resources/assets/src/scripts/skinlib.js @@ -1,38 +1,29 @@ /* * @Author: printempw * @Date: 2016-07-19 10:46:38 - * @Last Modified by: g-plane - * @Last Modified time: 2017-04-25 21:56:19 + * @Last Modified by: printempw + * @Last Modified time: 2017-06-30 10:25:12 */ 'use strict'; -$(document).ready(() => swal.setDefaults({ - confirmButtonText: trans('general.confirm'), - cancelButtonText: trans('general.cancel') -})); +$(document).ready(() => { + swal.setDefaults({ + confirmButtonText: trans('general.confirm'), + cancelButtonText: trans('general.cancel') + }); -$('#page-select').on('change', function () { - let sort = getQueryString('sort'), - page = getQueryString('page'), - filter = getQueryString('filter'); + if ($('#skinlib-container').length != 0) { + // Initially render skinlib + requestSkinlibData().then(result => { + renderSkinlib(result.items); - let targetPage = $(this).val(); - - // if has query strings - if (filter || sort) { - - if (page == null) { - // append "&page=" to current URL - window.location = `${location.href}&page=${targetPage}`; - } else { - window.location = `?filter=${filter}&sort=${sort}&page=${targetPage}`; - } - - } else { - window.location = `?page=${targetPage}`; + updatePaginator( + $.skinlib.page, + result.total_pages || 1 + ); + }); } - }); $('#private').on('ifToggled', function () { @@ -42,6 +33,223 @@ $('#type-skin').on('ifToggled', function () { $(this).prop('checked') ? $('#skin-type').show() : $('#skin-type').hide(); }); +$.skinlib = { + page: getQueryString('page', 1), + filter: getQueryString('filter', 'skin'), + sort: getQueryString('sort', 'time'), + uploader: getQueryString('uploader', 0), + keyword: decodeURI(getQueryString('keyword', '')) +}; + +$(document).ready(() => { + +}); + +function renderSkinlib(items) { + let container = $('#skinlib-container').html(''); + + if (items.length === 0) { + $('#skinlib-paginator').hide(); + + container.html(`

    + ${ trans('general.noResult') } +

    `); + } else { + $('#skinlib-paginator').show(); + + for (const item of items) { + container.append(renderSkinlibItemComponent(item)); + } + } + + $('.overlay').hide(); +} + +function reloadSkinlib() { + requestSkinlibData().then(result => { + $('.overlay').show(); + renderSkinlib(result.items); + + updatePaginator($.skinlib.page, result.total_pages || 1); + }).then(() => { + updateUrlQueryString(); + updateBreadCrumb(); + }); +} + +function requestSkinlibData() { + return Promise.resolve($.ajax({ + type: 'GET', + url: url('skinlib/data'), + dataType: 'json', + data: $.skinlib, + error: showAjaxError + })); +} + +function renderSkinlibItemComponent(item) { + let title = ""; + let anonymous = ""; + let liked = item.liked ? 'liked' : ''; + + if (item.liked === undefined) { + // If user haven't logged in + title = trans('skinlib.anonymous'); + anonymous = 'anonymous'; + } else { + title = item.liked ? trans('skinlib.removeFromCloset') : trans('skinlib.addToCloset'); + } + + return ` +
    +
    + +
    + +
    +
    + `; +} + +function updatePaginator(currentPage, totalPages) { + $.skinlib.page = currentPage; + + $('p.pagination').text(trans('general.pagination', { + page: currentPage, + total: totalPages + })); + + let paginator = $('#skinlib-paginator'); + + if (paginator.html().length == 0) { + // init paginator + $('#skinlib-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, { + currentPage: parseInt(currentPage), + totalPages: parseInt(totalPages), + onPageChange: onPageChange + })); + } else { + $('#skinlib-paginator').jqPaginator('option', { + currentPage: parseInt(currentPage), + totalPages: parseInt(totalPages) + }); + } + + let pageSelectElement = $('select.pagination').html(''); + + for (let i = 1; i <= totalPages; i++) { + pageSelectElement.append(` + + `); + } +} + +function onPageChange(page, type) { + $.skinlib.page = page; + updateBreadCrumb(); + + if (type == "init") { + console.log('Init paginator', page); + } else { + $('.overlay').show(); + reloadSkinlib(); + + console.log('Rendering page', page); + } +} + +$('select.pagination').on('change', function () { + onPageChange(parseInt($(this).val())); +}); + +$(document).on('click', '.more.like', function () { + let tid = $(this).attr('tid'); + + if ($(this).hasClass('anonymous')) + return; + + if ($(this).hasClass('liked')) { + removeFromCloset(tid); + } else { + addToCloset(tid); + } +}); + +$('.filter').click(function (e) { + e.preventDefault(); + let selectedFilter = $(this).data('filter'); + + if (selectedFilter == "uploader") { + $.skinlib.uploader = $(this).data('uid'); + console.log('Show items uploaded by uid ' + $.skinlib.uploader); + } else { + $.skinlib.filter = selectedFilter; + console.log('Filter by ' + $.skinlib.filter); + } + + reloadSkinlib(); +}); + +$('.sort').click(function (e) { + e.preventDefault(); + $.skinlib.sort = $(this).data('sort'); + + console.log('Sort by ' + $.skinlib.sort); + reloadSkinlib(); +}); + +$('#search-form').submit(function (e) { + e.preventDefault(); + $.skinlib.keyword = $('#navbar-search-input').val(); + + console.log('Search keyword: ' + $.skinlib.keyword); + reloadSkinlib(); +}); + +function updateUrlQueryString() { + let query = $.param($.skinlib); + + window.history.pushState(null, null, url(`skinlib?${query}`)); +} + +function updateBreadCrumb() { + if ($.skinlib.filter == "cape") { + $('#filter-indicator').html(trans('general.cape')); + } else { + $('#filter-indicator').html(trans('general.skin') + ` + ${ trans('skinlib.filter.' + $.skinlib.filter) } + `); + } + + if ($.skinlib.uploader != 0) { + $('#uploader-indicator').html(trans('skinlib.filter.uploader', {uid: $.skinlib.uploader})); + } else { + $('#uploader-indicator').html(trans('skinlib.filter.allUsers')); + } + + $('#sort-indicator').html(trans('skinlib.sort.' + $.skinlib.sort)); + + if ($.skinlib.keyword != "") { + $('#search-indicator').html(trans('general.searchResult', { + keyword: decodeURI($.skinlib.keyword) + })); + + $('#navbar-search-input').val(decodeURI($.skinlib.keyword)); + } +} + function addToCloset(tid) { $.getJSON(url(`skinlib/info/${tid}`), (json) => { swal({ @@ -274,7 +482,7 @@ function changeTextureName(tid, oldName) { }); } -$('.private-label').click(function () { +$(document).on('click', '.private-label', function () { swal({ text: trans('skinlib.setPublicNotice'), type: 'warning', diff --git a/resources/assets/src/scripts/user.js b/resources/assets/src/scripts/user.js index b0051226..88c017a8 100644 --- a/resources/assets/src/scripts/user.js +++ b/resources/assets/src/scripts/user.js @@ -261,20 +261,12 @@ $(document).ready(function() { if (window.location.pathname.includes('/user/closet')) { Promise.resolve($.ajax({ type: 'GET', - url: /(^https?.*)\/user\/closet/.exec(window.location.href)[1] + '/user/closet-data', + url: url('/user/closet-data'), dataType: 'json' })).then(result => { renderCloset(result.items, result.category); - $('#closet-paginator').jqPaginator({ + $('#closet-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, { totalPages: result.total_pages, - visiblePages: 5, - currentPage: 1, - first: '
  • «
  • ', - prev: '
  • ', - next: '
  • ', - last: '
  • »
  • ', - page: '
  • {{page}}
  • ', - wrapper: '', onPageChange: page => { reloadCloset( $('#skin-category').hasClass('active') ? 'skin' : 'cape', @@ -282,7 +274,7 @@ $(document).ready(function() { $('input[name=q]').val() ); } - }); + })); }).catch(error => showAjaxError); $('input[name=q]').on('input', debounce(() => { diff --git a/resources/assets/src/styles/general.scss b/resources/assets/src/styles/general.scss index 6547e05f..35ae4e34 100644 --- a/resources/assets/src/styles/general.scss +++ b/resources/assets/src/styles/general.scss @@ -40,13 +40,6 @@ input:-webkit-autofill { -webkit-box-shadow: 0 0 0px 1000px #fff inset !important; } -#page-select { - padding-left: 0; - margin: 0 20px 0 0; - border-radius: 4px; - padding: 5.5px 14px; -} - .item { display: block; background: #EFF1F0; diff --git a/resources/assets/src/styles/skinlib.scss b/resources/assets/src/styles/skinlib.scss index 5dc85161..174f6f4d 100644 --- a/resources/assets/src/styles/skinlib.scss +++ b/resources/assets/src/styles/skinlib.scss @@ -74,3 +74,32 @@ table { .table > tbody > tr > td { border-top: none; } + +select.pagination { + padding-left: 0; + margin: 0 20px 0 0; + border-radius: 4px; + padding: 5.5px 14px; +} + +.overlay span { + position: absolute; + top: 50%; + left: 50%; + margin-left: -40px; + margin-top: 25px; + color: #000; + font-size: 20px; +} + +#skinlib-container { + min-height: 20em; +} + +.private-label { + margin-top: 2px; +} + +.dropdown-menu .divider { + margin: 3px 0; +} diff --git a/resources/lang/en/general.yml b/resources/lang/en/general.yml index d144fd74..7cf2252a 100644 --- a/resources/lang/en/general.yml +++ b/resources/lang/en/general.yml @@ -27,7 +27,7 @@ close: Close skin: Skin cape: Cape -pagination: Page :page, total :total +loading: Loading texture-preview: Texture Preview walk: Walk diff --git a/resources/lang/en/locale.js b/resources/lang/en/locale.js index 70f0533f..2c880e1d 100644 --- a/resources/lang/en/locale.js +++ b/resources/lang/en/locale.js @@ -47,6 +47,20 @@ setNewTextureName: 'Please enter the new texture name:', emptyNewTextureName: 'Empty new texture name.', + // Skinlib + filter: { + skin: '(Any Model)', + steve: '(Steve)', + alex: '(Alex)', + cape: '(Cape)', + uploader: 'User (UID = :uid) Uploaded', + allUsers: 'All Users' + }, + sort: { + time: 'Newestly Uploaded', + likes: 'Most Likes' + }, + // Upload emptyTextureName: 'Empty texture name.', emptyTextureType: 'Please select a type for this texture.', @@ -173,6 +187,8 @@ confirm: 'OK', cancel: 'Cancel', more: 'More', + pagination: 'Page :page, total :total', + searchResult: '(Search result of keyword ":keyword")', noResult: 'No result.' } }; diff --git a/resources/lang/en/skinlib.yml b/resources/lang/en/skinlib.yml index 23db7b47..cd907acb 100644 --- a/resources/lang/en/skinlib.yml +++ b/resources/lang/en/skinlib.yml @@ -29,10 +29,6 @@ item: anonymous: Please login first private: Private -search: - title: 'Search Result: :query' - no-keywords: No keywords given - show: title: Texture Details anonymous: You must login to use closets diff --git a/resources/lang/zh_CN/general.yml b/resources/lang/zh_CN/general.yml index bcffc4f7..ef2c6f69 100644 --- a/resources/lang/zh_CN/general.yml +++ b/resources/lang/zh_CN/general.yml @@ -27,7 +27,7 @@ close: 关闭 skin: 皮肤 cape: 披风 -pagination: 第 :page 页,共 :total 页 +loading: 正在加载 texture-preview: 材质预览 walk: 行走 diff --git a/resources/lang/zh_CN/locale.js b/resources/lang/zh_CN/locale.js index 49f718dd..24a4066b 100644 --- a/resources/lang/zh_CN/locale.js +++ b/resources/lang/zh_CN/locale.js @@ -42,6 +42,22 @@ removeFromCloset: '从衣柜中移除', setItemName: '给你的皮肤起个名字吧~', emptyItemName: '你还没有填写要收藏的材质名称啊', + anonymous: '请先登录', + private: '私密', + + // Skinlib + filter: { + skin: '(任意模型)', + steve: '(Steve)', + alex: '(Alex)', + cape: '(披风)', + uploader: '用户(UID = :uid)上传', + allUsers: '所有用户' + }, + sort: { + time: '最新上传', + likes: '最多收藏' + }, // Rename setNewTextureName: '请输入新的材质名称:', @@ -173,6 +189,8 @@ confirm: '确定', cancel: '取消', more: '更多', + pagination: '第 :page 页,共 :total 页', + searchResult: '(关键词 “:keyword” 的搜索结果)', noResult: '无结果' }, vendor: { diff --git a/resources/lang/zh_CN/skinlib.yml b/resources/lang/zh_CN/skinlib.yml index c4e3eab1..9d1ce783 100644 --- a/resources/lang/zh_CN/skinlib.yml +++ b/resources/lang/zh_CN/skinlib.yml @@ -29,10 +29,6 @@ item: anonymous: 请先登录 private: 私密 -search: - title: 搜索结果::query - no-keywords: 未指定关键字 - show: title: 材质详情 anonymous: 登录后才能使用衣柜哦 diff --git a/resources/views/skinlib/index.tpl b/resources/views/skinlib/index.tpl index 5350d796..63d29af1 100644 --- a/resources/views/skinlib/index.tpl +++ b/resources/views/skinlib/index.tpl @@ -8,27 +8,33 @@
    -

    - {{ trans('general.skinlib') }} +

    {{ trans('general.skinlib') }} +

    - @include('vendor.breadcrumb') +
    -
    - @forelse ($textures as $texture) - tid") }}"> - @include('skinlib.item') - - @empty -

    {{ trans('skinlib.general.no-result') }}

    - @endforelse -
    + +
    + +
    + + {{ trans('general.loading') }}
    diff --git a/resources/views/skinlib/item.tpl b/resources/views/skinlib/item.tpl deleted file mode 100644 index 5d6259e4..00000000 --- a/resources/views/skinlib/item.tpl +++ /dev/null @@ -1,30 +0,0 @@ -
    -
    - -
    - - -
    diff --git a/resources/views/skinlib/master.tpl b/resources/views/skinlib/master.tpl index 93ef6d89..f6950c99 100644 --- a/resources/views/skinlib/master.tpl +++ b/resources/views/skinlib/master.tpl @@ -20,7 +20,9 @@