Skip to content

Commit

Permalink
Fix laggy global game selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
glacials committed Jan 16, 2020
1 parent 136d077 commit 348a2ef
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 96 deletions.
4 changes: 2 additions & 2 deletions app/assets/stylesheets/search.sass
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.twitter-typeahead, .tt-hint, .tt-input, .tt-menu
width: 75%
width: 100%

.twitter-typeahead, .tt-hint, .tt-input
.search, .tt-hint, .tt-input
display: block !important

14 changes: 1 addition & 13 deletions app/controllers/games_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,6 @@ def show
def edit
end

def update
if params[:sync_srdc]
@game.sync_with_srdc
end

if params[:sync_srl]
@game.sync_with_srdc
end

redirect_to edit_game_path(@game), notice: 'Done!'
end

private

def authorize
Expand All @@ -48,7 +36,7 @@ def set_game

def set_games
@games = Hash.new { |h, k| h[k] = [] }
SpeedrunDotComGame.order('ASCII(name) ASC, UPPER(name) ASC').each do |game|
Game.joins(:runs, :srdc).order('ASCII(games.name) ASC, UPPER(games.name) ASC').each do |game|
@games[game.name[0].downcase] << game
end
end
Expand Down
78 changes: 78 additions & 0 deletions app/javascript/game_select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import typeahead from "typeahead.js";
import Bloodhound from 'bloodhound-js'
import Handlebars from 'handlebars'

document.addEventListener('turbolinks:before-cache', function() {
$('.game-select').typeahead('destroy')
})

const loadGameSelector = function() {
$('.game-select').typeahead({
minLength: 3,
classNames: {
hint: 'text-muted',
menu: 'dropdown-menu',
selectable: 'dropdown-item',
cursor: 'active'
}
},
{
name: 'games',
display: game => game.name,
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.whitespace,
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/api/v4/games',
prepare: function(query, settings) {
return Object.assign(settings, {url: encodeURI(`${settings.url}?search=${query.trim()}`)})
},
transform: response => response.games.map(game => Object.assign(game, {'_type': 'game'})),
},
identify: game => game.id,
}),
templates: {
notFound: '<div class="dropdown-header"><h5>Games</h5></div><div class="dropdown-item disabled"><i>No games found</i></div>',
pending: '<div class="dropdown-header"><i>Searching games...</i></div>',
suggestion: Handlebars.compile(`
<div class="dropdown-item change-selected-game" data-id='{{id}}'>
{{name}}<br />
<small>{{categories.length}} categories</small>
</div>
`)
}
})
}

document.addEventListener('turbolinks:load', loadGameSelector)

document.addEventListener('click', function(event) {
if (!event.target.closest('.change-selected-game')) {
return
}

document.getElementById('selected-game-id').value = event.target.dataset['id']
document.getElementById('category-selector').focus()

const categorySelect = document.getElementById('category-selector')
const saveButton = document.getElementById('game-category-submit')
const loading = document.createElement('option')
loading.text = 'Loading...'

Array.from(categorySelect.children).forEach((option) => option.remove())
categorySelect.appendChild(loading)

fetch(`/api/v4/games?search=${document.getElementById('selected-game-id').value}`).then(response => response.json()).then(response => {
Array.from(categorySelect.children).forEach((option) => option.remove())
response.games[0].categories.forEach(category => {
const option = document.createElement('option')
option.value = category.id
option.text = category.name
categorySelect.appendChild(option)
})
saveButton.disabled = false
categorySelect.disabled = false
})
})

export { loadGameSelector }
2 changes: 1 addition & 1 deletion app/javascript/packs/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ import "../count.js"
import "../crisp.js"
import "../highchart_theme.js"
import "../chart_builder.js"
import "../game_select.js"
import "../landing.js"
import "../like.js"
import "../race_attach.js"
import "../run_claim.js"
import '../run_delete.js'
import '../run_disown.js'
import "../run_edit.js"
import "../run_export.js"
import "../run_parse.js"
import "../search.js"
Expand Down
33 changes: 0 additions & 33 deletions app/javascript/run_edit.js

This file was deleted.

27 changes: 21 additions & 6 deletions app/javascript/vue/race-title.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import raceNav from './race-nav.js'
import { getAccessToken } from '../token'
import { loadGameSelector } from '../game_select.js'
import VueBootstrapTypeahead from 'vue-bootstrap-typeahead'
const _ = require('underscore')

export default {
components: {
raceNav,
VueBootstrapTypeahead,
},
computed: {
categories: function() {
Expand All @@ -19,9 +23,6 @@ export default {
category: function() {
return this.categories.find(category => category.id === this.categoryId)
},
game: function() {
return this.games.find(game => game.id === this.gameId)
},
title: function() {
if (this.race === null) {
return ''
Expand All @@ -34,7 +35,6 @@ export default {
},
created: async function() {
this.notes = this.race.notes
fetch('/api/v4/games').then(response => response.json()).then(body => this.games = body.games)

this.gameId = (this.race.game || {id: null}).id
this.categoryId = (this.race.category || {id: null}).id
Expand All @@ -51,14 +51,22 @@ export default {

return entry.runner.id === this.currentUser.id
})

this.game = await fetch(`/api/v4/games?search=${this.race.game.id}`).then(response => response.json()).then(body => {
return body.games[0]
})

this.gameQuery = this.race.game.name
},
data: () => ({
categoryId: null,
editing: false,
entry: null,
error: null,
game: null,
gameId: null,
games: [],
gameQuery: null,
gameResults: [],
loading: false,
notes: '',
}),
Expand Down Expand Up @@ -105,13 +113,20 @@ export default {
}
},
},
mounted: function() {
},
name: 'race-title',
props: ['race', 'starting', 'syncing'],
watch: {
gameId: function() {
if (this.game.categories.find(category => category.id === this.categoryId) === undefined) {
if (!this.game || this.game.categories.find(category => category.id === this.categoryId) === undefined) {
this.categoryId = null
}
},
gameQuery: _.debounce(function(newGame) {
fetch(`/api/v4/games?search=${newGame}`).then(response => response.json()).then(body => {
this.gameResults = body.games
})
}, 500)
},
}
10 changes: 0 additions & 10 deletions app/views/games/edit.slim
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,3 @@
li If a category has an identical name to a #{@game} category, the two will be merged into one #{@game}
category.
li You must have moderation priveleges for both games to merge.
.col.p-1
.card.h-100
.card-header Resync
.card-body
= button_to game_path(@game), method: :patch, params: {'sync_srdc' => 1}, class: 'btn btn-srdc m-2'
=> image_tag(asset_path('srdc.png'), style: 'height: 0.8em')
' Sync with Speedrun.com
= button_to game_path(@game), method: :patch, params: {'sync_srl' => 1}, class: 'btn btn-light m-2'
=> image_tag(asset_path('srl.png'), style: 'height: 0.8em')
' Sync with SpeedRunsLive
13 changes: 8 additions & 5 deletions app/views/games/index.slim
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
- content_for(:title, 'Games')
.row: .col-md-12
.alert.m-3
' Searching #{number_with_delimiter(@games.sum(&:count))} games with at least one run.
' In total there are #{number_with_delimiter(Game.count)} games.
input#search.form-control.mb-4 type='text' placeholder='Search games...'
.card-columns.w-100
- @games.each do |label, srdc_games|
- @games.each do |label, games|
.card.game-section.w-100
.card-header = label
.list-group.list-group-flush
- srdc_games.each do |srdc_game|
- games.each do |game|
a.list-group-item.list-group-item-action.py-1.game.text-light(
href=game_path(srdc_game.shortname)
data={name: srdc_game.name}
) = srdc_game.name
href=game_path(game.srdc&.shortname)
data={name: game.name}
) = game.name
3 changes: 0 additions & 3 deletions app/views/layouts/application.slim
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ html
= form_for(:search, method: :get, url: search_path, html: {class: 'form-inline ml-2 flex-grow-1', role: 'search'}) do |f|
.input-group
= f.text_field(:q, name: :q, class: 'form-control search text-dark', value: @query, placeholder: 'Search...')
.input-group-append
= button_tag(type: 'submit', class: 'btn btn-secondary', name: nil) do
= icon('fas', 'search')
ul.nav.navbar-nav
- if current_user.present?
li.nav-item class=('active' if on_a_profile_page?)
Expand Down
19 changes: 13 additions & 6 deletions app/views/races/_title.slim
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,19 @@ race-title(
h1 v-if='!editing' = '{{title}}'
= form_for race, url: race_path(race), html: {'v-if' => 'editing'} do |f|
.form-row
.col-md-6.my-1
label.sr-only for='game-selector'
select.form-control v-model='gameId'
option v-for='game in games' :value='game.id' {{game.name}}
.col-md-6.my-1
label.sr-only for='category-selector'
= f.hidden_field(:game, id: 'selected-game-id', value: race.game&.id)
.my-3.col-md-12
label for='game-selector' Game
vue-bootstrap-typeahead.mb-1(
background-variant='dark'
placeholder='Change game by typing...'
text-variant='light'
:data='gameResults'
@hit='game = $event'
:serializer='game => game.name'
v-model='gameQuery'
)
label for='category-selector' Category
select.form-control v-model='categoryId'
option v-for='category in categories' :value='category.id' {{category.name}}
.row
Expand Down
16 changes: 3 additions & 13 deletions app/views/runs/edit.slim
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,10 @@
h5.card-header Edit Game/Category
.card-body
.form-row
= f.hidden_field(:game, id: 'selected-game-id', value: @run.game&.id)
.col-md-6.my-1
label.sr-only for='game-selector'
= f.collection_select( \
:game, \
SpeedrunDotComGame.order(:name), \
:shortname, \
:name, \
{ \
selected: @run.game.try(:srdc).try(:shortname), \
include_blank: true \
}, \
id: 'game-selector', \
class: 'form-control' \
)
= f.text_field(:q, name: :q, class: 'form-control game-select text-dark', value: @run.game&.name, placeholder: 'Game...')
.col-md-6.my-1
label.sr-only for='category-selector'
= f.collection_select( \
Expand All @@ -44,7 +34,7 @@
:id, \
:name, \
{ \
selected: @run.category.try(:id) \
selected: @run.category&.id, \
}, \
id: 'category-selector', \
class: 'form-control' \
Expand Down
3 changes: 0 additions & 3 deletions app/views/search/index.slim
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
.form-group
.input-group
= f.text_field(:q, name: :q, autofocus: @query.blank?, value: @query, class: 'form-control search bg-dark')
.input-group-append
= button_tag(type: 'submit', class: 'btn btn-primary', name: nil) do
= icon('fas', 'search')

#search-results.card.mb-3.w-100
- @results.each do |search_type, results|
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"typeahead.js": "^0.11.1",
"underscore": "^1.8.3",
"vue": "^2.6.10",
"vue-bootstrap-typeahead": "^0.2.6",
"vue-loader": "^15.7.0",
"vue-multiselect": "^2.1.6",
"vue-template-compiler": "^2.6.10",
Expand Down
Loading

0 comments on commit 348a2ef

Please sign in to comment.