jam-ui: fix pagination in musician listing

This commit is contained in:
Nuwan 2022-03-18 19:27:48 +05:30
parent 245b39f67d
commit ee54464cfc
13 changed files with 116 additions and 66 deletions

View File

@ -311,6 +311,7 @@
"genres": []
}
],
"offset": 11,
"page_count": 2,
"my_audio_latency": 5,
"filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",

View File

@ -285,6 +285,7 @@
"genres": []
}
],
"offset": null,
"page_count": 2,
"my_audio_latency": 5,
"filter_json": "{\"id\":\"68dcc055-cb5d-40d6-8ed4-66772d1a1a31\",\"user_id\":\"27bd4a30-d1b8-4eea-8454-01a104d59381\",\"foreign_key1_id\":null,\"data_blob\":{\"sort_order\":\"latency\",\"instruments\":[],\"genres\":[],\"concert_gigs\":\"-1\",\"interests\":\"any\",\"studio_sessions\":\"-1\",\"ages\":[],\"skill_level\":\"-1\"}}",

View File

@ -124,15 +124,15 @@ describe('Friends page without data', () => {
});
describe('Friends page with data', () => {
describe.only('Friends page with data', () => {
beforeEach(() => {
cy.stubAuthenticate({ id: '2' }); //currentUser id is 2 - people.yaml fixture
cy.intercept('POST', /\S+\/filter\?page=1/, { fixture: 'people_page1' }).as('getPeople_page1');
cy.intercept('POST', /\S+\/filter\?page=2/, { fixture: 'people_page2' }).as('getPeople_page2');
cy.intercept('POST', /\S+\/filter\?page=0/, { fixture: 'people_page1' }).as('getPeople_page1');
cy.intercept('POST', /\S+\/filter\?page=1/, { fixture: 'people_page2' }).as('getPeople_page2');
cy.intercept('GET', /\S+\/profile\S+/, { fixture: 'person' });
});
describe('listing users', () => {
describe.only('listing users', () => {
beforeEach(() => {
cy.visit('/friends');
});
@ -142,6 +142,13 @@ describe('Friends page with data', () => {
cy.viewport('macbook-13');
});
it('paginate', () => {
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 10);
cy.get('[data-testid=paginate-next-page]').click();
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 20);
cy.get('[data-testid=paginate-next-page]').should('not.exist');
});
it('show profiles', () => {
cy.contains('Find New Friends').should('exist');
cy.contains('Update Search').should('exist');
@ -219,12 +226,7 @@ describe('Friends page with data', () => {
closeSidePanel();
});
it('paginate', () => {
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 10);
cy.get('[data-testid=paginate-next-page]').click();
cy.get('[data-testid=peopleListTable] > tbody tr').should('have.length', 20);
cy.get('[data-testid=paginate-next-page]').should('not.exist');
});
});
describe('in mobile', () => {
@ -276,18 +278,9 @@ describe('Friends page with data', () => {
.should('not.contain', 'More');
});
it('click more button', () => {
cy.get('[data-testid=peopleSwiper] .swiper-slide')
.first()
.find('[data-testid=btnMore]')
.click();
showSidePanelContent();
closeSidePanel();
});
//it.skip('click connect button', () => {});
it('click connect button', () => {});
it('click message button', () => {});
//it.skip('click message button', () => {});
it('paginate', () => {
cy.get('[data-testid=peopleSwiper] .swiper-button-prev').should('have.class', 'swiper-button-disabled');
@ -297,6 +290,15 @@ describe('Friends page with data', () => {
}
cy.get('[data-testid=peopleSwiper] .swiper-button-next').should('have.class', 'swiper-button-disabled');
});
it('click more button', () => {
cy.get('[data-testid=peopleSwiper] .swiper-slide')
.first()
.find('[data-testid=btnMore]')
.click();
showSidePanelContent();
closeSidePanel();
});
});
});

View File

@ -19,19 +19,23 @@ function JKPeopleFilter() {
const { t } = useTranslation();
const [show, setShow] = useState(false);
const [resetFilter, setResetFilter] = useState(false);
const [showLoadMore, setShowLoadMore] = useState(false)
const [instruments, setInstruments] = useState([]);
const [genres, setGenres] = useState([]);
const dispatch = useDispatch();
const currentPage = useRef(1);
const nextPage = useRef(1);
const [hasNextPage, setHasNextPage] = useState(true)
const currentPage = useRef(0);
const nextPage = useRef(0);
const perPageLimit = 20
const { greaterThan } = useResponsive();
const peopleListRef = useRef();
const people = useSelector(state => state.people.people);
const totalPages = useSelector(state => state.people.totalPages);
const hasNextPage = useSelector(state => state.people.hasNextPage);
//const offset = useSelector(state => state.people.offset);
const loadingStatus = useSelector(state => state.people.status);
const { register, handleSubmit, setValue, getValues, control } = useForm({
@ -113,7 +117,7 @@ function JKPeopleFilter() {
};
const goNextPage = () => {
submitPageQuery(currentPage.current)
submitPageQuery()
};
const clearFilterOpts = () => {
@ -136,13 +140,18 @@ function JKPeopleFilter() {
const submitForm = event => {
event.preventDefault();
currentPage.current = 1;
nextPage.current = 1;
currentPage.current = 0;
nextPage.current = 0;
dispatch(resetState());
handleSubmit(onSubmit)(nextPage.current);
handleSubmit(onSubmit)();
setShow(false);
};
const isBeforeSecondPageLoad = () => {
return currentPage.current === 0 && nextPage.current === 1
}
const onSubmit = (data) => {
let genres = [];
let joined_within_days = '';
@ -165,12 +174,12 @@ function JKPeopleFilter() {
currentPage.current = nextPage.current
dispatch(fetchPeople({ data: params, page: currentPage.current }));
dispatch(fetchPeople({ data: params, page: currentPage.current, limit: perPageLimit }));
nextPage.current = currentPage.current + 1
if (hasNextPage) {
dispatch(preFetchPeople({ data: params, page: nextPage.current }));
if (hasNextPage || isBeforeSecondPageLoad()) { //reason for isBeforeSecondPageLoad(): after the first fetchPeople() there is a possibility that hasNextPage may not been sat in redux store.
dispatch(preFetchPeople({ data: params, page: nextPage.current, limit: perPageLimit }));
nextPage.current = nextPage.current + 1;
}
} catch (error) {
@ -181,36 +190,38 @@ function JKPeopleFilter() {
useEffect(() => {
fetchGenres();
fetchInstruments();
submitPageQuery(currentPage.current)
currentPage.current = 0
submitPageQuery()
}, []);
useEffect(() => {
if(totalPages > 0){
setHasNextPage(currentPage.current < totalPages)
}
}, [totalPages, currentPage.current]);
useEffect(() => {
if (resetFilter) {
clearFilterOpts();
dispatch(resetState());
currentPage.current = 1;
nextPage.current = 1;
currentPage.current = 0;
nextPage.current = 0;
handleSubmit(onSubmit)();
setResetFilter(false);
}
}, [resetFilter]);
useEffect(() => {
if(loadingStatus === 'succeeded' && people.length === 0 && currentPage.current === 1 && !getValues("from_location")){
//no results found. let's fetch again with from_location enabled
setValue('from_location', true);
nextPage.current = 1
currentPage.current = 1
submitPageQuery()
setShowLoadMore(hasNextPage)
if(loadingStatus === 'succeeded'){
if(people.length === 0 && currentPage.current === 0 && !getValues("from_location")){
//no results found. let's fetch again with from_location enabled
setValue('from_location', true);
nextPage.current = 0
currentPage.current = 0
submitPageQuery()
}
}
}, [loadingStatus])
return (
<Card>
<FalconCardHeader title={t('page_title', { ns: 'people' })} titleClass="font-weight-bold">
@ -419,7 +430,7 @@ function JKPeopleFilter() {
<JKPeopleList
people={people}
goNextPage={goNextPage}
hasNext={hasNextPage}
hasNext={showLoadMore}
isLoading={loadingStatus === 'loading' && people.length !== 0}
/>
</div>

View File

@ -26,6 +26,8 @@ const JKPeopleList = ({ people, goNextPage, hasNext, isLoading }) => {
))}
</tbody>
</Table>
{hasNext && (
<Button color="primary" outline={true} onClick={() => goNextPage()} disabled={isLoading} data-testid="paginate-next-page">

View File

@ -24,9 +24,9 @@ export const getPersonById = (id) => {
))
}
export const getPeople = ({ data, page } = {}) => {
export const getPeople = ({ data, page, limit } = {}) => {
return new Promise((resolve, reject) => {
apiFetch(`/filter?page=${page}`, {
apiFetch(`/filter?page=${page}&limit=${limit}`, {
method: 'POST',
body: JSON.stringify(data)
})

View File

@ -7,6 +7,7 @@ const initialState = {
status: 'idel',
error: null,
totalPages: 0,
hasNextPage: false
}
export const fetchPeople = createAsyncThunk(
@ -68,7 +69,10 @@ export const peopleSlice = createSlice({
},
loadPrefetched: (state, action) => {
if(state.prefetched.length > 0){
state.people = [...state.people, ...state.prefetched]
const records = new Set([...state.people, ...state.prefetched]);
const unique = [];
records.map(x => unique.filter(a => a.id === x.id).length > 0 ? null : unique.push(x))
state.people = unique
}
state.prefetched = []
}
@ -79,23 +83,33 @@ export const peopleSlice = createSlice({
state.status = 'loading'
})
.addCase(fetchPeople.fulfilled, (state, action) => {
console.log("DEBUG: fetchPeople action.payload", action.payload);
//console.log("DEBUG: action.meta.arg", action.meta.arg);
const records = new Set([...state.people, ...action.payload.musicians]);
const unique = [];
records.map(x => unique.filter(a => a.id === x.id).length > 0 ? null : unique.push(x))
state.people = unique
state.totalPages = action.payload.page_count
state.hasNextPage = action.payload.offset && parseInt(action.payload.offset) > 0
state.status = 'succeeded'
})
.addCase(fetchPeople.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
.addCase(preFetchPeople.pending, (state, action) => {
})
.addCase(preFetchPeople.fulfilled, (state, action) => {
state.prefetched = action.payload.musicians
console.log("DEBUG: preFetchPeople action.payload", action.payload);
const records = new Set([...state.prefetched, ...action.payload.musicians]);
const unique = [];
records.map(x => unique.filter(a => a.id === x.id).length > 0 ? null : unique.push(x))
state.prefetched = unique
state.hasNextPage = action.payload.offset && parseInt(action.payload.offset) > 0
})
.addCase(preFetchPeople.rejected, (state, action) => {
})
.addCase(acceptFriendRequest.fulfilled, (state, action) => {
})
.addCase(fetchPerson.fulfilled, (state, action) => {
const person = state.people.find(person => person.id === action.payload.id)

View File

@ -200,6 +200,12 @@ module JamRuby
rel
end
def user_search_results(user_ids)
rel = do_filter(user_ids)
rel = self.search_includes(rel)
process_results_page(rel.all)
end
def search_results_page(filter=nil, page=1, user_ids=nil)
if filter
self.data_blob = filter

View File

@ -176,7 +176,7 @@ module JamRuby
#NOTE: we can change to this once we upgrade postgresql
#rel = rel.where(id: user_ids).where('users.id <> ?', self.user.id).order("array_position(ARRAY[#{user_ids.map { |i| "'#{i}'" }.join(',')}], id::TEXT)")
rel = self._sort_by_ids_ordinality(rel, user_ids)
#rel = self._sort_by_ids_ordinality(rel, user_ids)
end
end
rel

View File

@ -100,8 +100,8 @@ class ApiSearchController < ApiController
latency_good = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_good])
latency_fair = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_fair])
latency_high = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_high])
page = [params[:page].to_i, 1].max
limit = 20
page = [params[:page].to_i, 0].max
limit = [params[:limit].to_i, 20].max
filter_params = {}
filter_params.merge!(from_location: params[:from_location] ? '1' : '0')
@ -143,7 +143,10 @@ class ApiSearchController < ApiController
begin
#bm = Benchmark.measure do
@latency_data = users_latency_data(latency_good, latency_fair, latency_high, filter_params, page, limit)
result = users_latency_data(latency_good, latency_fair, latency_high, filter_params, page, limit)
@latency_data = result[:data]
@offset = result[:offset]
user_ids = @latency_data.map{ |l_data| l_data[:user_id] }
#end
@ -154,7 +157,8 @@ class ApiSearchController < ApiController
# end if Rails.env.production?
sobj = MusicianSearch.user_search_filter(current_user)
@search = sobj.search_results_page(filter_params, page, user_ids)
#@search = sobj.search_results_page(filter_params, page, user_ids)
@search = sobj.user_search_results(user_ids)
respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/filter'

View File

@ -11,6 +11,7 @@ module LatencyHelper
};
def users_latency_data(latency_good, latency_fair, latency_high, filter_opts, page, limit)
latency_data = []
uri = URI(filter_latency_url)
@ -21,13 +22,15 @@ module LatencyHelper
req["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
req["Content-Type"] = "application/json"
offset = page.to_i <= 1 ? 0 : ((page.to_i - 1) * limit) + 1
req_params = {
my_user_id: current_user.id,
my_public_ip: request.remote_ip,
my_device_id: nil,
my_client_id: nil,
from_location: filter_opts[:from_location] || "0",
offset: page,
offset: offset,
limit: limit
}
@ -39,9 +42,13 @@ module LatencyHelper
req.body = req_params.to_json
response = http.request(req)
#debugger
if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPSuccess)
graph_db_users = JSON.parse(response.body)["users"]
json_body = JSON.parse(response.body)
graph_db_users = json_body['users']
offset = json_body['next']
if latency_good || latency_fair || latency_high
#fiter by latency params
@ -62,7 +69,7 @@ module LatencyHelper
}
}.uniq
return latency_data
return {data: latency_data, offset: offset}
else
logger.debug("Latency response failed: #{response}")
Bugsnag.notify("LatencyResponseFailed") do |report|

View File

@ -1,9 +1,13 @@
object @search
node :page_count do |foo|
@search.page_count
node :offset do
@offset
end
# node :page_count do |foo|
# @search.page_count
# end
node :my_audio_latency do |user|
current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
end

View File

@ -215,13 +215,11 @@ describe "Musician Filter API", type: :request do
end
it "filter musicians by days ago that they joined" do
pending
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, joined_within_days: 1 }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
end
it "finds user updated_at is within a day ago" do
pending
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, active_within_days: 1 }
expect(JSON.parse(response.body)["musicians"].size).to eq(1)
end