mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-09-20 14:45:56 +08:00
setup new pagination for activities
This commit is contained in:
parent
3b1cb51be9
commit
219ef3baf6
|
@ -2,7 +2,6 @@ module ClientApi
|
|||
class ActivitiesController < ApplicationController
|
||||
|
||||
def index
|
||||
@timezone = current_user.time_zone
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render template: '/client_api/activities/index',
|
||||
|
@ -15,11 +14,17 @@ module ClientApi
|
|||
private
|
||||
|
||||
def activities_vars
|
||||
last_activity_id = params[:from].to_i || 0
|
||||
per_page = Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT
|
||||
activities = current_user.last_activities(last_activity_id, per_page + 1)
|
||||
more = activities.length > per_page
|
||||
{ activities: activities, more: more }
|
||||
page = (params[:page] || 1).to_i
|
||||
activities = current_user
|
||||
.last_activities
|
||||
.page(page)
|
||||
.per(Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT)
|
||||
{
|
||||
activities: activities,
|
||||
page: page,
|
||||
more: !current_user.last_activities.page(page).last_page?,
|
||||
timezone: current_user.time_zone
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ const ActivityElement = ({ activity }: Activity ): Node => (
|
|||
<StyledLi>
|
||||
<TimeSpan>
|
||||
<FormattedDate
|
||||
value={new Date(activity.created_at)}
|
||||
value={new Date(activity.createdAt)}
|
||||
hour="2-digit"
|
||||
minute="2-digit"
|
||||
timeZone={activity.timezone}
|
||||
|
|
|
@ -53,7 +53,8 @@ type Props = {
|
|||
|
||||
type State = {
|
||||
activities: Array<Activity>,
|
||||
more: boolean
|
||||
more: boolean,
|
||||
currentPage: number
|
||||
};
|
||||
|
||||
class GlobalActivitiesModal extends Component<Props, State> {
|
||||
|
@ -70,7 +71,7 @@ class GlobalActivitiesModal extends Component<Props, State> {
|
|||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { activities: [], more: false };
|
||||
this.state = { activities: [], more: false, currentPage: 1 };
|
||||
(this: any).displayActivities = this.displayActivities.bind(this);
|
||||
(this: any).addMoreActivities = this.addMoreActivities.bind(this);
|
||||
(this: any).onCloseModalActions = this.onCloseModalActions.bind(this);
|
||||
|
@ -79,7 +80,7 @@ class GlobalActivitiesModal extends Component<Props, State> {
|
|||
}
|
||||
|
||||
onCloseModalActions(): void {
|
||||
this.setState({ activities: [], more: false });
|
||||
this.setState({ activities: [], more: false, currentPage: 1 });
|
||||
this.props.onCloseModal();
|
||||
}
|
||||
|
||||
|
@ -87,16 +88,16 @@ class GlobalActivitiesModal extends Component<Props, State> {
|
|||
getActivities().then(response => {
|
||||
this.setState({
|
||||
activities: response.activities,
|
||||
more: response.more
|
||||
more: response.more,
|
||||
currentPage: response.currentPage
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
mapActivities(): Array<*> {
|
||||
return this.state.activities.map((activity, i, arr) => {
|
||||
// @todo replace key={i} with key={activity.id} !!!!!!!!!!!!!!
|
||||
// when the backend bug will be fixed
|
||||
const newDate = new Date(activity.created_at);
|
||||
const newDate = new Date(activity.createdAt);
|
||||
// returns a label with "today" if the date of the activity is today
|
||||
if (i === 0) {
|
||||
return GlobalActivitiesModal.renderActivityDateElement(
|
||||
|
@ -107,7 +108,7 @@ class GlobalActivitiesModal extends Component<Props, State> {
|
|||
}
|
||||
// else checks if the previous activity is newer than current
|
||||
// and displays a label with the date
|
||||
const prevDate = new Date(arr[i - 1].created_at);
|
||||
const prevDate = new Date(arr[i - 1].createdAt);
|
||||
if (prevDate.getDate() > newDate.getDate()) {
|
||||
return GlobalActivitiesModal.renderActivityDateElement(
|
||||
i,
|
||||
|
@ -116,7 +117,7 @@ class GlobalActivitiesModal extends Component<Props, State> {
|
|||
);
|
||||
}
|
||||
// returns the default activity element
|
||||
return <ActivityElement key={i} activity={activity} />;
|
||||
return <ActivityElement key={activity.id} activity={activity} />;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,15 +137,21 @@ class GlobalActivitiesModal extends Component<Props, State> {
|
|||
}
|
||||
|
||||
addMoreActivities(): void {
|
||||
const lastId = _.last(this.state.activities).id;
|
||||
getActivities(
|
||||
lastId
|
||||
).then((response: { activities: Array<Activity>, more: boolean }) => {
|
||||
this.setState({
|
||||
activities: [...this.state.activities, ...response.activities],
|
||||
more: response.more
|
||||
});
|
||||
});
|
||||
this.state.currentPage + 1
|
||||
).then(
|
||||
(response: {
|
||||
activities: Array<Activity>,
|
||||
more: boolean,
|
||||
currentPage: number
|
||||
}) => {
|
||||
this.setState({
|
||||
activities: [...this.state.activities, ...response.activities],
|
||||
more: response.more,
|
||||
currentPage: response.currentPage
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
addMoreButton(): Element<*> {
|
||||
|
|
|
@ -14,9 +14,9 @@ import { getSciNoteInfo } from "../../../services/api/configurations_api";
|
|||
import AboutScinoteModal from "./AboutScinoteModal";
|
||||
|
||||
type State = {
|
||||
modalOpen: boolean,
|
||||
scinoteVersion: string,
|
||||
addons: Array<string>
|
||||
addons: Array<string>,
|
||||
showModal: boolean
|
||||
};
|
||||
|
||||
class InfoDropdown extends Component<*, State> {
|
||||
|
|
|
@ -3,8 +3,8 @@ import axiosInstance from "./config";
|
|||
import { ACTIVITIES_PATH } from "./endpoints";
|
||||
|
||||
export function getActivities(
|
||||
lastId: number = 0
|
||||
page: number = 1
|
||||
): Promise<*> {
|
||||
const path = `${ACTIVITIES_PATH}?from=${lastId}`;
|
||||
const path = `${ACTIVITIES_PATH}?page=${page}`;
|
||||
return axiosInstance.get(path).then(({ data }) => data.global_activities);
|
||||
}
|
||||
|
|
|
@ -324,24 +324,24 @@ class User < ApplicationRecord
|
|||
# Finds all activities of user that is assigned to project. If user
|
||||
# is not an owner of the project, user must be also assigned to
|
||||
# module.
|
||||
def last_activities(last_activity_id = nil,
|
||||
per_page = Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT)
|
||||
last_activity_id = Constants::INFINITY if last_activity_id < 1
|
||||
def last_activities
|
||||
Activity
|
||||
.joins(project: :user_projects)
|
||||
.joins("LEFT OUTER JOIN my_modules ON activities.my_module_id = my_modules.id")
|
||||
.joins("LEFT OUTER JOIN user_my_modules ON my_modules.id = user_my_modules.my_module_id")
|
||||
.where('activities.id < ?', last_activity_id)
|
||||
.joins(
|
||||
'LEFT OUTER JOIN my_modules ON activities.my_module_id = my_modules.id'
|
||||
)
|
||||
.joins(
|
||||
'LEFT OUTER JOIN user_my_modules ON my_modules.id = ' \
|
||||
'user_my_modules.my_module_id'
|
||||
)
|
||||
.where(user_projects: { user_id: self })
|
||||
.where(
|
||||
'activities.my_module_id IS NULL OR ' +
|
||||
'user_projects.role = 0 OR ' +
|
||||
'activities.my_module_id IS NULL OR ' \
|
||||
'user_projects.role = 0 OR ' \
|
||||
'user_my_modules.user_id = ?',
|
||||
id
|
||||
)
|
||||
.order(created_at: :desc)
|
||||
.limit(per_page)
|
||||
.uniq
|
||||
end
|
||||
|
||||
def self.find_by_valid_wopi_token(token)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
json.global_activities do
|
||||
json.more more
|
||||
json.currentPage page
|
||||
json.activities activities do |activity|
|
||||
json.id activity.id
|
||||
json.message activity.message
|
||||
json.created_at activity.created_at
|
||||
json.timezone @timezone
|
||||
json.createdAt activity.created_at
|
||||
json.timezone timezone
|
||||
end
|
||||
end
|
||||
|
|
3
flow-typed/types.js
vendored
3
flow-typed/types.js
vendored
|
@ -33,7 +33,8 @@ export type ValidationErrors = string | Array<string> | Array<ValidationError>;
|
|||
export type Activity = {
|
||||
id?: number,
|
||||
message: string,
|
||||
created_at: string
|
||||
createdAt: string,
|
||||
timezone: string
|
||||
};
|
||||
|
||||
export type State = {
|
||||
|
|
20
spec/controllers/client_api/activities_controller_spec.rb
Normal file
20
spec/controllers/client_api/activities_controller_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe ClientApi::ActivitiesController, type: :controller do
|
||||
login_user
|
||||
render_views
|
||||
|
||||
before do
|
||||
project = create :project, created_by: User.first
|
||||
UserProject.create(user: User.first, project: project, role: 2)
|
||||
create :activity, user: User.first, project: project
|
||||
end
|
||||
|
||||
describe '#index' do
|
||||
it 'returns a valid object' do
|
||||
get :index, format: :json
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.body).to match_response_schema('activities')
|
||||
end
|
||||
end
|
||||
end
|
7
spec/factories/activity.rb
Normal file
7
spec/factories/activity.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
FactoryGirl.define do
|
||||
factory :activity do
|
||||
type_of :create_project
|
||||
message Faker::Lorem.sentence(10)
|
||||
project { Project.first || create(:project) }
|
||||
end
|
||||
end
|
26
spec/support/api/schemas/activities.json
Normal file
26
spec/support/api/schemas/activities.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": ["global_activities"],
|
||||
"properties": {
|
||||
"global_activities": {
|
||||
"type": "object",
|
||||
"required": ["activities", "more", "currentPage"],
|
||||
"properties": {
|
||||
"more": { "type": "boolean" },
|
||||
"currentPage": { "type": "integer" },
|
||||
"activities": {
|
||||
"type": "array",
|
||||
"items":{
|
||||
"required": ["id", "message", "createdAt", "timezone"],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"message": { "type": "string" },
|
||||
"createdAt": { "type": "string" },
|
||||
"timezone": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue