Merge branch 'patron' into 'develop'

Show Patron donor badge, fixes #238

Closes #238

See merge request soapbox-pub/soapbox-fe!94
tl-language-filters
Alex Gleason 2020-07-05 02:36:42 +00:00
commit 6f46b6a323
8 zmienionych plików z 103 dodań i 14 usunięć

Wyświetl plik

@ -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,
};
}

Wyświetl plik

@ -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])),
}; };
}; };

Wyświetl plik

@ -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 }));
} }
} }

Wyświetl plik

@ -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()),
}; };
}; };

Wyświetl plik

@ -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')),

Wyświetl plik

@ -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,
},
},
}));
});
});
}); });

Wyświetl plik

@ -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;
} }

Wyświetl plik

@ -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);
}); });
}); });
}; };