Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combofeeds #2675

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controllers/api/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class Api::BaseController < ApplicationController
DEFAULT_STATUSES_LIMIT = 20
DEFAULT_ACCOUNTS_LIMIT = 40
DEFAULT_TAGS_LIMIT = 40

include Api::RateLimitHeaders
include Api::AccessTokenTrackingConcern
Expand Down
100 changes: 100 additions & 0 deletions app/controllers/api/v1/lists/tags_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

class Api::V1::Lists::TagsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:show]
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show]

before_action :require_user!
before_action :set_list

after_action :insert_pagination_headers, only: :show

def show
@tags = load_tags
render json: @tags, each_serializer: REST::TagSerializer
end

def create
ApplicationRecord.transaction do
list_tags.each do |tag|
@list.tags << tag
end
end
@tags = load_tags
render json: @tags, each_serializer: REST::TagSerializer
end

def destroy
ListTag.where(list: @list, tag_id: tag_ids).destroy_all
render_empty
end

private

def set_list
@list = List.where(account: current_account).find(params[:list_id])
end

def load_tags
if unlimited?
@list.tags.all
else
@list.tags.paginate_by_max_id(limit_param(DEFAULT_TAGS_LIMIT), params[:max_id], params[:since_id])
end
end

def list_tags
names = tag_ids.grep_v(/\A[0-9]+\Z/)
ids = tag_ids.grep(/\A[0-9]+\Z/)
existing_by_name = Tag.where(name: names.map { |n| Tag.normalize(n) }).select(:id, :name)
ids.push(*existing_by_name.map(&:id))
not_existing_by_name = names.reject { |n| existing_by_name.any? { |e| e.name == Tag.normalize(n) } }
created = Tag.find_or_create_by_names(not_existing_by_name)
ids.push(*created.map(&:id))
Tag.find(ids)
end

def tag_ids
Array(resource_params[:tag_ids])
end

def resource_params
params.permit(tag_ids: [])
end

def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end

def next_path
return if unlimited?

api_v1_list_tags_url pagination_params(max_id: pagination_max_id) if records_continue?
end

def prev_path
return if unlimited?

api_v1_list_tags_url pagination_params(since_id: pagination_since_id) unless @tags.empty?
end

def pagination_max_id
@tags.last.id
end

def pagination_since_id
@tags.first.id
end

def records_continue?
@tags.size == limit_param(DEFAULT_TAGS_LIMIT)
end

def pagination_params(core_params)
params.slice(:limit).permit(:limit).merge(core_params)
end

def unlimited?
params[:limit] == '0'
end
end
16 changes: 16 additions & 0 deletions app/javascript/flavours/glitch/actions/importer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const STATUS_IMPORT = 'STATUS_IMPORT';
export const STATUSES_IMPORT = 'STATUSES_IMPORT';
export const POLLS_IMPORT = 'POLLS_IMPORT';
export const FILTERS_IMPORT = 'FILTERS_IMPORT';
export const TAGS_IMPORT = 'TAGS_IMPORT';

function pushUnique(array, object) {
if (array.every(element => element.id !== object.id)) {
Expand All @@ -29,6 +30,10 @@ export function importPolls(polls) {
return { type: POLLS_IMPORT, polls };
}

export function importTags(tags) {
return { type: TAGS_IMPORT, tags };
}

export function importFetchedAccount(account) {
return importFetchedAccounts([account]);
}
Expand All @@ -49,6 +54,17 @@ export function importFetchedAccounts(accounts) {
return importAccounts({ accounts: normalAccounts });
}

export function importFetchedTags(tags) {
return (dispatch) => {
const uniqueTags = [];
function processTag(tag) {
pushUnique(uniqueTags, tag);
}
tags.forEach(processTag);
dispatch(importTags(uniqueTags));
};
}

export function importFetchedStatus(status) {
return importFetchedStatuses([status]);
}
Expand Down
107 changes: 79 additions & 28 deletions app/javascript/flavours/glitch/actions/lists.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import api from '../api';

import { showAlertForError } from './alerts';
import { importFetchedAccounts } from './importer';
import { importFetchedAccounts, importFetchedTags } from './importer';

export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
Expand Down Expand Up @@ -31,6 +31,10 @@ export const LIST_ACCOUNTS_FETCH_REQUEST = 'LIST_ACCOUNTS_FETCH_REQUEST';
export const LIST_ACCOUNTS_FETCH_SUCCESS = 'LIST_ACCOUNTS_FETCH_SUCCESS';
export const LIST_ACCOUNTS_FETCH_FAIL = 'LIST_ACCOUNTS_FETCH_FAIL';

export const LIST_TAGS_FETCH_REQUEST = 'LIST_TAGS_FETCH_REQUEST';
export const LIST_TAGS_FETCH_SUCCESS = 'LIST_TAGS_FETCH_SUCCESS';
export const LIST_TAGS_FETCH_FAIL = 'LIST_TAGS_FETCH_FAIL';

export const LIST_EDITOR_SUGGESTIONS_CHANGE = 'LIST_EDITOR_SUGGESTIONS_CHANGE';
export const LIST_EDITOR_SUGGESTIONS_READY = 'LIST_EDITOR_SUGGESTIONS_READY';
export const LIST_EDITOR_SUGGESTIONS_CLEAR = 'LIST_EDITOR_SUGGESTIONS_CLEAR';
Expand Down Expand Up @@ -118,6 +122,7 @@ export const setupListEditor = listId => (dispatch, getState) => {
});

dispatch(fetchListAccounts(listId));
dispatch(fetchListTags(listId));
};

export const changeListEditorTitle = value => ({
Expand Down Expand Up @@ -234,6 +239,33 @@ export const fetchListAccountsFail = (id, error) => ({
error,
});

export const fetchListTags = listId => (dispatch, getState) => {
dispatch(fetchListTagsRequest(listId));

api(getState).get(`/api/v1/lists/${listId}/tags`, { params: { limit: 0 } }).then(({ data }) => {
dispatch(importFetchedTags(data));
dispatch(fetchListTagsSuccess(listId, data));
}).catch(err => dispatch(fetchListTagsFail(listId, err)));
};

export const fetchListTagsFail = (id, error) => ({
type: LIST_TAGS_FETCH_FAIL,
id,
error,
});

export const fetchListTagsRequest = id => ({
type: LIST_TAGS_FETCH_REQUEST,
id,
});

export const fetchListTagsSuccess = (id, tags, next) => ({
type: LIST_TAGS_FETCH_SUCCESS,
id,
tags,
next,
});

export const fetchListSuggestions = q => (dispatch, getState) => {
const params = {
q,
Expand Down Expand Up @@ -263,65 +295,84 @@ export const changeListSuggestions = value => ({
value,
});

export const addToListEditor = accountId => (dispatch, getState) => {
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId));
export const addToListEditor = (id, type) => (dispatch, getState) => {
dispatch(addToList(getState().getIn(['listEditor', 'listId']), id, type));
};

export const addToList = (listId, accountId) => (dispatch, getState) => {
dispatch(addToListRequest(listId, accountId));

api(getState).post(`/api/v1/lists/${listId}/accounts`, { account_ids: [accountId] })
.then(() => dispatch(addToListSuccess(listId, accountId)))
.catch(err => dispatch(addToListFail(listId, accountId, err)));
export const addToList = (listId, id, type) => (dispatch, getState) => {
dispatch(addToListRequest(listId, id, type));

if ('tags' === type) {
api(getState).post(`/api/v1/lists/${listId}/tags`, { tag_ids: [id] })
.then((data) => dispatch(addToListSuccess(listId, id, type, data)))
.catch(err => dispatch(addToListFail(listId, id, type, err)));
} else {
api(getState).post(`/api/v1/lists/${listId}/accounts`, { account_ids: [id] })
.then(() => dispatch(addToListSuccess(listId, id, type)))
.catch(err => dispatch(addToListFail(listId, id, type, err)));
}
};

export const addToListRequest = (listId, accountId) => ({
export const addToListRequest = (listId, id, type) => ({
type: LIST_EDITOR_ADD_REQUEST,
addType: type,
listId,
accountId,
id,
});

export const addToListSuccess = (listId, accountId) => ({
export const addToListSuccess = (listId, id, type, data) => ({
type: LIST_EDITOR_ADD_SUCCESS,
addType: type,
listId,
accountId,
id,
data,
});

export const addToListFail = (listId, accountId, error) => ({
export const addToListFail = (listId, id, type, error) => ({
type: LIST_EDITOR_ADD_FAIL,
addType: type,
listId,
accountId,
id,
error,
});

export const removeFromListEditor = accountId => (dispatch, getState) => {
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId));
export const removeFromListEditor = (id, type) => (dispatch, getState) => {
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), id, type));
};

export const removeFromList = (listId, accountId) => (dispatch, getState) => {
dispatch(removeFromListRequest(listId, accountId));
export const removeFromList = (listId, id, type) => (dispatch, getState) => {
dispatch(removeFromListRequest(listId, id, type));

api(getState).delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [accountId] } })
.then(() => dispatch(removeFromListSuccess(listId, accountId)))
.catch(err => dispatch(removeFromListFail(listId, accountId, err)));
if ('tags' === type) {
api(getState).delete(`/api/v1/lists/${listId}/tags`, { params: { tag_ids: [id] } })
.then(() => dispatch(removeFromListSuccess(listId, id, type)))
.catch(err => dispatch(removeFromListFail(listId, id, type, err)));
} else {
api(getState).delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [id] } })
.then(() => dispatch(removeFromListSuccess(listId, id, type)))
.catch(err => dispatch(removeFromListFail(listId, id, type, err)));
}
};

export const removeFromListRequest = (listId, accountId) => ({
export const removeFromListRequest = (listId, id, type) => ({
type: LIST_EDITOR_REMOVE_REQUEST,
removeType: type,
listId,
accountId,
id,
});

export const removeFromListSuccess = (listId, accountId) => ({
export const removeFromListSuccess = (listId, id, type) => ({
type: LIST_EDITOR_REMOVE_SUCCESS,
removeType: type,
listId,
accountId,
id,
});

export const removeFromListFail = (listId, accountId, error) => ({
export const removeFromListFail = (listId, id, type, error) => ({
type: LIST_EDITOR_REMOVE_FAIL,
removeType: type,
listId,
accountId,
id,
error,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const makeMapStateToProps = () => {
};

const mapDispatchToProps = (dispatch, { accountId }) => ({
onRemove: () => dispatch(removeFromListEditor(accountId)),
onAdd: () => dispatch(addToListEditor(accountId)),
onRemove: () => dispatch(removeFromListEditor(accountId, 'accounts')),
onAdd: () => dispatch(addToListEditor(accountId, 'accounts')),
});

class Account extends ImmutablePureComponent {
Expand Down
Loading
Loading