sforkowany z mirror/soapbox
Merge branch 'patron' into 'develop'
Show Patron donor badge, fixes #238 Closes #238 See merge request soapbox-pub/soapbox-fe!94tl-language-filters
commit
6f46b6a323
|
@ -2,30 +2,61 @@ import api from '../api';
|
||||||
|
|
||||||
export const PATRON_INSTANCE_FETCH_REQUEST = 'PATRON_INSTANCE_FETCH_REQUEST';
|
export const PATRON_INSTANCE_FETCH_REQUEST = 'PATRON_INSTANCE_FETCH_REQUEST';
|
||||||
export const PATRON_INSTANCE_FETCH_SUCCESS = 'PATRON_INSTANCE_FETCH_SUCCESS';
|
export const PATRON_INSTANCE_FETCH_SUCCESS = 'PATRON_INSTANCE_FETCH_SUCCESS';
|
||||||
export const PATRON_INSTANCE_FETCH_FAIL = 'PATRON_INSTANCE_FETCH_FAIL';
|
export const PATRON_INSTANCE_FETCH_FAIL = 'PATRON_INSTANCE_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const PATRON_ACCOUNT_FETCH_REQUEST = 'PATRON_ACCOUNT_FETCH_REQUEST';
|
||||||
|
export const PATRON_ACCOUNT_FETCH_SUCCESS = 'PATRON_ACCOUNT_FETCH_SUCCESS';
|
||||||
|
export const PATRON_ACCOUNT_FETCH_FAIL = 'PATRON_ACCOUNT_FETCH_FAIL';
|
||||||
|
|
||||||
export function fetchPatronInstance() {
|
export function fetchPatronInstance() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch({ type: PATRON_INSTANCE_FETCH_REQUEST });
|
dispatch({ type: PATRON_INSTANCE_FETCH_REQUEST });
|
||||||
api(getState).get('/api/patron/v1/instance').then(response => {
|
api(getState).get('/api/patron/v1/instance').then(response => {
|
||||||
dispatch(importFetchedFunding(response.data));
|
dispatch(importFetchedInstance(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchFundingFail(error));
|
dispatch(fetchInstanceFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function importFetchedFunding(instance) {
|
export function fetchPatronAccount(apId) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
apId = encodeURIComponent(apId);
|
||||||
|
dispatch({ type: PATRON_ACCOUNT_FETCH_REQUEST });
|
||||||
|
api(getState).get(`/api/patron/v1/accounts/${apId}`).then(response => {
|
||||||
|
dispatch(importFetchedAccount(response.data));
|
||||||
|
}).catch(error => {
|
||||||
|
dispatch(fetchAccountFail(error));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function importFetchedInstance(instance) {
|
||||||
return {
|
return {
|
||||||
type: PATRON_INSTANCE_FETCH_SUCCESS,
|
type: PATRON_INSTANCE_FETCH_SUCCESS,
|
||||||
instance,
|
instance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchFundingFail(error) {
|
function fetchInstanceFail(error) {
|
||||||
return {
|
return {
|
||||||
type: PATRON_INSTANCE_FETCH_FAIL,
|
type: PATRON_INSTANCE_FETCH_FAIL,
|
||||||
error,
|
error,
|
||||||
skipAlert: true,
|
skipAlert: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function importFetchedAccount(account) {
|
||||||
|
return {
|
||||||
|
type: PATRON_ACCOUNT_FETCH_SUCCESS,
|
||||||
|
account,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchAccountFail(error) {
|
||||||
|
return {
|
||||||
|
type: PATRON_ACCOUNT_FETCH_FAIL,
|
||||||
|
error,
|
||||||
|
skipAlert: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
account: getAccount(state, me),
|
account: getAccount(state, me),
|
||||||
sidebarOpen: state.get('sidebar').sidebarOpen,
|
sidebarOpen: state.get('sidebar').sidebarOpen,
|
||||||
donateUrl: state.getIn(['patron', 'url']),
|
donateUrl: state.getIn(['patron', 'instance', 'url']),
|
||||||
isStaff: isStaff(state.getIn(['accounts', me])),
|
isStaff: isStaff(state.getIn(['accounts', me])),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
||||||
import MissingIndicator from 'soapbox/components/missing_indicator';
|
import MissingIndicator from 'soapbox/components/missing_indicator';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import { fetchPatronAccount } from '../../actions/patron';
|
||||||
|
|
||||||
const emptyList = ImmutableList();
|
const emptyList = ImmutableList();
|
||||||
|
|
||||||
|
@ -23,12 +24,14 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
|
|
||||||
let accountId = -1;
|
let accountId = -1;
|
||||||
let accountUsername = username;
|
let accountUsername = username;
|
||||||
|
let accountApId = null;
|
||||||
if (accountFetchError) {
|
if (accountFetchError) {
|
||||||
accountId = null;
|
accountId = null;
|
||||||
} else {
|
} else {
|
||||||
let account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase());
|
let account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase());
|
||||||
accountId = account ? account.getIn(['id'], null) : -1;
|
accountId = account ? account.getIn(['id'], null) : -1;
|
||||||
accountUsername = account ? account.getIn(['acct'], '') : '';
|
accountUsername = account ? account.getIn(['acct'], '') : '';
|
||||||
|
accountApId = account ? account.get('url') : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = withReplies ? `${accountId}:with_replies` : accountId;
|
const path = withReplies ? `${accountId}:with_replies` : accountId;
|
||||||
|
@ -40,12 +43,14 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
accountId,
|
accountId,
|
||||||
unavailable,
|
unavailable,
|
||||||
accountUsername,
|
accountUsername,
|
||||||
|
accountApId,
|
||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
||||||
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
||||||
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||||
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
||||||
me,
|
me,
|
||||||
|
patronEnabled: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,7 +70,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { params: { username }, accountId, withReplies, me } = this.props;
|
const { params: { username }, accountId, accountApId, withReplies, me, patronEnabled } = this.props;
|
||||||
|
|
||||||
if (accountId && accountId !== -1) {
|
if (accountId && accountId !== -1) {
|
||||||
this.props.dispatch(fetchAccount(accountId));
|
this.props.dispatch(fetchAccount(accountId));
|
||||||
|
@ -75,6 +80,10 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
|
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (patronEnabled && accountApId) {
|
||||||
|
this.props.dispatch(fetchPatronAccount(accountApId));
|
||||||
|
}
|
||||||
|
|
||||||
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
|
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
|
||||||
} else {
|
} else {
|
||||||
this.props.dispatch(fetchAccountByUsername(username));
|
this.props.dispatch(fetchAccountByUsername(username));
|
||||||
|
@ -82,7 +91,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { me, accountId, withReplies } = this.props;
|
const { me, accountId, withReplies, accountApId, patronEnabled } = this.props;
|
||||||
if (accountId && accountId !== -1 && (accountId !== prevProps.accountId && accountId) || withReplies !== prevProps.withReplies) {
|
if (accountId && accountId !== -1 && (accountId !== prevProps.accountId && accountId) || withReplies !== prevProps.withReplies) {
|
||||||
this.props.dispatch(fetchAccount(accountId));
|
this.props.dispatch(fetchAccount(accountId));
|
||||||
if (me) this.props.dispatch(fetchAccountIdentityProofs(accountId));
|
if (me) this.props.dispatch(fetchAccountIdentityProofs(accountId));
|
||||||
|
@ -91,6 +100,10 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
|
this.props.dispatch(expandAccountFeaturedTimeline(accountId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (patronEnabled && accountApId) {
|
||||||
|
this.props.dispatch(fetchPatronAccount(accountApId));
|
||||||
|
}
|
||||||
|
|
||||||
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
|
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { injectIntl } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import ProgressBar from '../../../components/progress_bar';
|
import ProgressBar from '../../../components/progress_bar';
|
||||||
import { fetchPatronInstance } from 'soapbox/actions/patron';
|
import { fetchPatronInstance } from 'soapbox/actions/patron';
|
||||||
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
const moneyFormat = amount => (
|
const moneyFormat = amount => (
|
||||||
new Intl
|
new Intl
|
||||||
|
@ -63,7 +64,7 @@ class FundingPanel extends ImmutablePureComponent {
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
patron: state.get('patron'),
|
patron: state.getIn(['patron', 'instance'], ImmutableMap()),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,12 @@ import SignUpPanel from '../features/ui/components/sign_up_panel';
|
||||||
import ProfileInfoPanel from '../features/ui/components/profile_info_panel';
|
import ProfileInfoPanel from '../features/ui/components/profile_info_panel';
|
||||||
import { acctFull } from 'soapbox/utils/accounts';
|
import { acctFull } from 'soapbox/utils/accounts';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
import { makeGetAccount } from '../selectors';
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
||||||
const accounts = state.getIn(['accounts']);
|
const accounts = state.getIn(['accounts']);
|
||||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase());
|
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase());
|
||||||
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
let accountId = -1;
|
let accountId = -1;
|
||||||
let account = null;
|
let account = null;
|
||||||
|
@ -30,7 +32,7 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
//Children components fetch information
|
//Children components fetch information
|
||||||
|
|
||||||
return {
|
return {
|
||||||
account,
|
account: accountId ? getAccount(state, accountId) : account,
|
||||||
accountId,
|
accountId,
|
||||||
accountUsername,
|
accountUsername,
|
||||||
features: getFeatures(state.get('instance')),
|
features: getFeatures(state.get('instance')),
|
||||||
|
|
|
@ -1,8 +1,29 @@
|
||||||
import reducer from '../patron';
|
import reducer from '../patron';
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { PATRON_ACCOUNT_FETCH_SUCCESS } from '../../actions/patron';
|
||||||
|
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||||
|
|
||||||
describe('patron reducer', () => {
|
describe('patron reducer', () => {
|
||||||
it('should return the initial state', () => {
|
it('should return the initial state', () => {
|
||||||
expect(reducer(undefined, {})).toEqual(ImmutableMap());
|
expect(reducer(undefined, {})).toEqual(ImmutableMap());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PATRON_ACCOUNT_FETCH_SUCCESS', () => {
|
||||||
|
it('should add the account', () => {
|
||||||
|
const action = {
|
||||||
|
type: PATRON_ACCOUNT_FETCH_SUCCESS,
|
||||||
|
account: {
|
||||||
|
url: 'https://gleasonator.com/users/alex',
|
||||||
|
is_patron: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const state = ImmutableMap();
|
||||||
|
expect(reducer(state, action)).toEqual(fromJS({
|
||||||
|
accounts: {
|
||||||
|
'https://gleasonator.com/users/alex': {
|
||||||
|
is_patron: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
import { PATRON_INSTANCE_FETCH_SUCCESS } from '../actions/patron';
|
import {
|
||||||
|
PATRON_INSTANCE_FETCH_SUCCESS,
|
||||||
|
PATRON_ACCOUNT_FETCH_SUCCESS,
|
||||||
|
} from '../actions/patron';
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||||
|
|
||||||
const initialState = ImmutableMap();
|
const initialState = ImmutableMap();
|
||||||
|
|
||||||
|
const normalizePatronAccount = (state, account) => {
|
||||||
|
const normalized = fromJS(account).deleteAll(['url']);
|
||||||
|
return state.setIn(['accounts', account.url], normalized);
|
||||||
|
};
|
||||||
|
|
||||||
export default function patron(state = initialState, action) {
|
export default function patron(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case PATRON_INSTANCE_FETCH_SUCCESS:
|
case PATRON_INSTANCE_FETCH_SUCCESS:
|
||||||
return fromJS(action.instance);
|
return state.set('instance', fromJS(action.instance));
|
||||||
|
case PATRON_ACCOUNT_FETCH_SUCCESS:
|
||||||
|
return normalizePatronAccount(state, action.account);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,19 @@ const getAccountBase = (state, id) => state.getIn(['accounts', id], null
|
||||||
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
||||||
const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
|
const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null);
|
||||||
const getAccountMoved = (state, id) => state.getIn(['accounts', state.getIn(['accounts', id, 'moved'])]);
|
const getAccountMoved = (state, id) => state.getIn(['accounts', state.getIn(['accounts', id, 'moved'])]);
|
||||||
|
const getAccountPatron = (state, id) => {
|
||||||
|
const url = state.getIn(['accounts', id, 'url']);
|
||||||
|
return state.getIn(['patron', 'accounts', url]);
|
||||||
|
};
|
||||||
|
|
||||||
export const makeGetAccount = () => {
|
export const makeGetAccount = () => {
|
||||||
return createSelector([getAccountBase, getAccountCounters, getAccountRelationship, getAccountMoved], (base, counters, relationship, moved) => {
|
return createSelector([
|
||||||
|
getAccountBase,
|
||||||
|
getAccountCounters,
|
||||||
|
getAccountRelationship,
|
||||||
|
getAccountMoved,
|
||||||
|
getAccountPatron,
|
||||||
|
], (base, counters, relationship, moved, patron) => {
|
||||||
if (base === null) {
|
if (base === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +25,7 @@ export const makeGetAccount = () => {
|
||||||
return base.merge(counters).withMutations(map => {
|
return base.merge(counters).withMutations(map => {
|
||||||
map.set('relationship', relationship);
|
map.set('relationship', relationship);
|
||||||
map.set('moved', moved);
|
map.set('moved', moved);
|
||||||
|
map.set('patron', patron);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
Ładowanie…
Reference in New Issue