Change hashtags and mentions in bios to open in-app in web UI (#24643)
parent
e9a79d46cd
commit
8099ba04be
|
@ -135,8 +135,7 @@ export const showSearch = () => ({
|
||||||
type: SEARCH_SHOW,
|
type: SEARCH_SHOW,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const openURL = routerHistory => (dispatch, getState) => {
|
export const openURL = (value, history, onFailure) => (dispatch, getState) => {
|
||||||
const value = getState().getIn(['search', 'value']);
|
|
||||||
const signedIn = !!getState().getIn(['meta', 'me']);
|
const signedIn = !!getState().getIn(['meta', 'me']);
|
||||||
|
|
||||||
if (!signedIn) {
|
if (!signedIn) {
|
||||||
|
@ -148,15 +147,21 @@ export const openURL = routerHistory => (dispatch, getState) => {
|
||||||
api(getState).get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => {
|
api(getState).get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => {
|
||||||
if (response.data.accounts?.length > 0) {
|
if (response.data.accounts?.length > 0) {
|
||||||
dispatch(importFetchedAccounts(response.data.accounts));
|
dispatch(importFetchedAccounts(response.data.accounts));
|
||||||
routerHistory.push(`/@${response.data.accounts[0].acct}`);
|
history.push(`/@${response.data.accounts[0].acct}`);
|
||||||
} else if (response.data.statuses?.length > 0) {
|
} else if (response.data.statuses?.length > 0) {
|
||||||
dispatch(importFetchedStatuses(response.data.statuses));
|
dispatch(importFetchedStatuses(response.data.statuses));
|
||||||
routerHistory.push(`/@${response.data.statuses[0].account.acct}/${response.data.statuses[0].id}`);
|
history.push(`/@${response.data.statuses[0].account.acct}/${response.data.statuses[0].id}`);
|
||||||
|
} else if (onFailure) {
|
||||||
|
onFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(fetchSearchSuccess(response.data, value));
|
dispatch(fetchSearchSuccess(response.data, value));
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
dispatch(fetchSearchFail(err));
|
dispatch(fetchSearchFail(err));
|
||||||
|
|
||||||
|
if (onFailure) {
|
||||||
|
onFailure();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,7 @@ class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
identity: PropTypes.object,
|
identity: PropTypes.object,
|
||||||
|
router: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -101,11 +102,16 @@ class Header extends ImmutablePureComponent {
|
||||||
onChangeLanguages: PropTypes.func.isRequired,
|
onChangeLanguages: PropTypes.func.isRequired,
|
||||||
onInteractionModal: PropTypes.func.isRequired,
|
onInteractionModal: PropTypes.func.isRequired,
|
||||||
onOpenAvatar: PropTypes.func.isRequired,
|
onOpenAvatar: PropTypes.func.isRequired,
|
||||||
|
onOpenURL: PropTypes.func.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.node = c;
|
||||||
|
};
|
||||||
|
|
||||||
openEditProfile = () => {
|
openEditProfile = () => {
|
||||||
window.open('/settings/profile', '_blank');
|
window.open('/settings/profile', '_blank');
|
||||||
};
|
};
|
||||||
|
@ -162,6 +168,61 @@ class Header extends ImmutablePureComponent {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleHashtagClick = e => {
|
||||||
|
const { router } = this.context;
|
||||||
|
const value = e.currentTarget.textContent.replace(/^#/, '');
|
||||||
|
|
||||||
|
if (router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
router.history.push(`/tags/${value}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleMentionClick = e => {
|
||||||
|
const { router } = this.context;
|
||||||
|
const { onOpenURL } = this.props;
|
||||||
|
|
||||||
|
if (router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const link = e.currentTarget;
|
||||||
|
|
||||||
|
onOpenURL(link.href, router.history, () => {
|
||||||
|
window.location = link.href;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_attachLinkEvents () {
|
||||||
|
const node = this.node;
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = node.querySelectorAll('a');
|
||||||
|
|
||||||
|
let link;
|
||||||
|
|
||||||
|
for (var i = 0; i < links.length; ++i) {
|
||||||
|
link = links[i];
|
||||||
|
|
||||||
|
if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
|
||||||
|
link.addEventListener('click', this.handleHashtagClick, false);
|
||||||
|
} else if (link.classList.contains('mention')) {
|
||||||
|
link.addEventListener('click', this.handleMentionClick, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this._attachLinkEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate () {
|
||||||
|
this._attachLinkEvents();
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, hidden, intl, domain } = this.props;
|
const { account, hidden, intl, domain } = this.props;
|
||||||
const { signedIn, permissions } = this.context.identity;
|
const { signedIn, permissions } = this.context.identity;
|
||||||
|
@ -360,7 +421,7 @@ class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
{!(suspended || hidden) && (
|
{!(suspended || hidden) && (
|
||||||
<div className='account__header__extra'>
|
<div className='account__header__extra'>
|
||||||
<div className='account__header__bio'>
|
<div className='account__header__bio' ref={this.setRef}>
|
||||||
{(account.get('id') !== me && signedIn) && <AccountNoteContainer account={account} />}
|
{(account.get('id') !== me && signedIn) && <AccountNoteContainer account={account} />}
|
||||||
|
|
||||||
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />}
|
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />}
|
||||||
|
|
|
@ -26,6 +26,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onChangeLanguages: PropTypes.func.isRequired,
|
onChangeLanguages: PropTypes.func.isRequired,
|
||||||
onInteractionModal: PropTypes.func.isRequired,
|
onInteractionModal: PropTypes.func.isRequired,
|
||||||
onOpenAvatar: PropTypes.func.isRequired,
|
onOpenAvatar: PropTypes.func.isRequired,
|
||||||
|
onOpenURL: PropTypes.func.isRequired,
|
||||||
hideTabs: PropTypes.bool,
|
hideTabs: PropTypes.bool,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
|
@ -137,6 +138,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onChangeLanguages={this.handleChangeLanguages}
|
onChangeLanguages={this.handleChangeLanguages}
|
||||||
onInteractionModal={this.handleInteractionModal}
|
onInteractionModal={this.handleInteractionModal}
|
||||||
onOpenAvatar={this.handleOpenAvatar}
|
onOpenAvatar={this.handleOpenAvatar}
|
||||||
|
onOpenURL={this.props.onOpenURL}
|
||||||
domain={this.props.domain}
|
domain={this.props.domain}
|
||||||
hidden={hidden}
|
hidden={hidden}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
pinAccount,
|
pinAccount,
|
||||||
unpinAccount,
|
unpinAccount,
|
||||||
} from '../../../actions/accounts';
|
} from '../../../actions/accounts';
|
||||||
|
import { openURL } from 'mastodon/actions/search';
|
||||||
import {
|
import {
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
directCompose,
|
directCompose,
|
||||||
|
@ -159,6 +160,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onOpenURL (url, routerHistory, onFailure) {
|
||||||
|
dispatch(openURL(url, routerHistory, onFailure));
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
||||||
|
|
|
@ -161,9 +161,9 @@ class Search extends React.PureComponent {
|
||||||
|
|
||||||
handleURLClick = () => {
|
handleURLClick = () => {
|
||||||
const { router } = this.context;
|
const { router } = this.context;
|
||||||
const { onOpenURL } = this.props;
|
const { value, onOpenURL } = this.props;
|
||||||
|
|
||||||
onOpenURL(router.history);
|
onOpenURL(value, router.history);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleStatusSearch = () => {
|
handleStatusSearch = () => {
|
||||||
|
|
|
@ -34,8 +34,8 @@ const mapDispatchToProps = dispatch => ({
|
||||||
dispatch(showSearch());
|
dispatch(showSearch());
|
||||||
},
|
},
|
||||||
|
|
||||||
onOpenURL (routerHistory) {
|
onOpenURL (q, routerHistory) {
|
||||||
dispatch(openURL(routerHistory));
|
dispatch(openURL(q, routerHistory));
|
||||||
},
|
},
|
||||||
|
|
||||||
onClickSearchResult (q, type) {
|
onClickSearchResult (q, type) {
|
||||||
|
|
Reference in New Issue