Merge branch 'shorthand_apub_id_searching' into federation
This commit is contained in:
commit
75c6c8521b
10 changed files with 67 additions and 28 deletions
2
README.md
vendored
2
README.md
vendored
|
@ -73,7 +73,7 @@ Each lemmy server can set its own moderation policy; appointing site-wide admins
|
||||||
- Full vote scores `(+/-)` like old reddit.
|
- Full vote scores `(+/-)` like old reddit.
|
||||||
- Themes, including light, dark, and solarized.
|
- Themes, including light, dark, and solarized.
|
||||||
- Emojis with autocomplete support. Start typing `:`
|
- Emojis with autocomplete support. Start typing `:`
|
||||||
- User tagging using `@`, Community tagging using `#`.
|
- User tagging using `@`, Community tagging using `!`.
|
||||||
- Integrated image uploading in both posts and comments.
|
- Integrated image uploading in both posts and comments.
|
||||||
- A post can consist of a title and any combination of self text, a URL, or nothing else.
|
- A post can consist of a title and any combination of self text, a URL, or nothing else.
|
||||||
- Notifications, on comment replies and when you're tagged.
|
- Notifications, on comment replies and when you're tagged.
|
||||||
|
|
2
docs/src/about_guide.md
vendored
2
docs/src/about_guide.md
vendored
|
@ -3,7 +3,7 @@
|
||||||
Start typing...
|
Start typing...
|
||||||
|
|
||||||
- `@a_user_name` to get a list of usernames.
|
- `@a_user_name` to get a list of usernames.
|
||||||
- `#a_community` to get a list of communities.
|
- `!a_community` to get a list of communities.
|
||||||
- `:emoji` to get a list of emojis.
|
- `:emoji` to get a list of emojis.
|
||||||
|
|
||||||
## Sorting
|
## Sorting
|
||||||
|
|
|
@ -45,11 +45,36 @@ pub enum SearchAcceptedObjects {
|
||||||
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
|
||||||
///
|
///
|
||||||
/// Some working examples for use with the docker/federation/ setup:
|
/// Some working examples for use with the docker/federation/ setup:
|
||||||
/// http://lemmy_alpha:8540/c/main
|
/// http://lemmy_alpha:8540/c/main, or !main@lemmy_alpha:8540
|
||||||
/// http://lemmy_alpha:8540/u/lemmy_alpha
|
/// http://lemmy_alpha:8540/u/lemmy_alpha, or @lemmy_alpha@lemmy_alpha:8540
|
||||||
/// http://lemmy_alpha:8540/p/3
|
/// http://lemmy_alpha:8540/p/3
|
||||||
pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchResponse, Error> {
|
pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchResponse, Error> {
|
||||||
let query_url = Url::parse(&query)?;
|
// Parse the shorthand query url
|
||||||
|
let query_url = if query.contains('@') {
|
||||||
|
debug!("{}", query);
|
||||||
|
let split = query.split('@').collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
// User type will look like ['', username, instance]
|
||||||
|
// Community will look like [!community, instance]
|
||||||
|
let (name, instance) = if split.len() == 3 {
|
||||||
|
(format!("/u/{}", split[1]), split[2])
|
||||||
|
} else if split.len() == 2 {
|
||||||
|
if split[0].contains('!') {
|
||||||
|
let split2 = split[0].split('!').collect::<Vec<&str>>();
|
||||||
|
(format!("/c/{}", split2[1]), split[1])
|
||||||
|
} else {
|
||||||
|
return Err(format_err!("Invalid search query: {}", query));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format_err!("Invalid search query: {}", query));
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = format!("{}://{}{}", get_apub_protocol_string(), instance, name);
|
||||||
|
Url::parse(&url)?
|
||||||
|
} else {
|
||||||
|
Url::parse(&query)?
|
||||||
|
};
|
||||||
|
|
||||||
let mut response = SearchResponse {
|
let mut response = SearchResponse {
|
||||||
type_: SearchType::All.to_string(),
|
type_: SearchType::All.to_string(),
|
||||||
comments: vec![],
|
comments: vec![],
|
||||||
|
|
4
ui/src/api_tests/api.spec.ts
vendored
4
ui/src/api_tests/api.spec.ts
vendored
|
@ -95,7 +95,9 @@ describe('main', () => {
|
||||||
describe('follow_accept', () => {
|
describe('follow_accept', () => {
|
||||||
test('/u/lemmy_alpha follows and accepts lemmy_beta/c/main', async () => {
|
test('/u/lemmy_alpha follows and accepts lemmy_beta/c/main', async () => {
|
||||||
// Make sure lemmy_beta/c/main is cached on lemmy_alpha
|
// Make sure lemmy_beta/c/main is cached on lemmy_alpha
|
||||||
let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy_beta:8550/c/main&type_=All&sort=TopAll`;
|
// Use short-hand search url
|
||||||
|
let searchUrl = `${lemmyAlphaApiUrl}/search?q=!main@lemmy_beta:8550&type_=All&sort=TopAll`;
|
||||||
|
|
||||||
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
}).then(d => d.json());
|
}).then(d => d.json());
|
||||||
|
|
7
ui/src/components/community-link.tsx
vendored
7
ui/src/components/community-link.tsx
vendored
|
@ -12,6 +12,7 @@ interface CommunityOther {
|
||||||
|
|
||||||
interface CommunityLinkProps {
|
interface CommunityLinkProps {
|
||||||
community: Community | CommunityOther;
|
community: Community | CommunityOther;
|
||||||
|
realLink?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommunityLink extends Component<CommunityLinkProps, any> {
|
export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||||
|
@ -27,8 +28,10 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||||
name_ = community.name;
|
name_ = community.name;
|
||||||
link = `/c/${community.name}`;
|
link = `/c/${community.name}`;
|
||||||
} else {
|
} else {
|
||||||
name_ = `${hostname(community.actor_id)}/${community.name}`;
|
name_ = `${community.name}@${hostname(community.actor_id)}`;
|
||||||
link = `/community/${community.id}`;
|
link = !this.props.realLink
|
||||||
|
? `/community/${community.id}`
|
||||||
|
: community.actor_id;
|
||||||
}
|
}
|
||||||
return <Link to={link}>{name_}</Link>;
|
return <Link to={link}>{name_}</Link>;
|
||||||
}
|
}
|
||||||
|
|
1
ui/src/components/search.tsx
vendored
1
ui/src/components/search.tsx
vendored
|
@ -109,7 +109,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
nextProps.history.action == 'POP' ||
|
nextProps.history.action == 'POP' ||
|
||||||
nextProps.history.action == 'PUSH'
|
nextProps.history.action == 'PUSH'
|
||||||
) {
|
) {
|
||||||
this.state = this.emptyState;
|
|
||||||
this.state.q = this.getSearchQueryFromProps(nextProps);
|
this.state.q = this.getSearchQueryFromProps(nextProps);
|
||||||
this.state.type_ = this.getSearchTypeFromProps(nextProps);
|
this.state.type_ = this.getSearchTypeFromProps(nextProps);
|
||||||
this.state.sort = this.getSortTypeFromProps(nextProps);
|
this.state.sort = this.getSortTypeFromProps(nextProps);
|
||||||
|
|
13
ui/src/components/sidebar.tsx
vendored
13
ui/src/components/sidebar.tsx
vendored
|
@ -11,6 +11,7 @@ import { WebSocketService, UserService } from '../services';
|
||||||
import { mdToHtml, getUnixTime, hostname } from '../utils';
|
import { mdToHtml, getUnixTime, hostname } from '../utils';
|
||||||
import { CommunityForm } from './community-form';
|
import { CommunityForm } from './community-form';
|
||||||
import { UserListing } from './user-listing';
|
import { UserListing } from './user-listing';
|
||||||
|
import { CommunityLink } from './community-link';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
|
@ -66,7 +67,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
name_ = community.name;
|
name_ = community.name;
|
||||||
link = `/c/${community.name}`;
|
link = `/c/${community.name}`;
|
||||||
} else {
|
} else {
|
||||||
name_ = `${hostname(community.actor_id)}/${community.name}`;
|
name_ = `${community.name}@${hostname(community.actor_id)}`;
|
||||||
link = community.actor_id;
|
link = community.actor_id;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -86,15 +87,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
</h5>
|
</h5>
|
||||||
{community.local ? (
|
<CommunityLink community={community} realLink />
|
||||||
<Link className="text-muted" to={link}>
|
|
||||||
{name_}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<a className="text-muted" href={link} target="_blank">
|
|
||||||
{name_}
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
||||||
{this.canMod && (
|
{this.canMod && (
|
||||||
<>
|
<>
|
||||||
|
|
5
ui/src/components/user-listing.tsx
vendored
5
ui/src/components/user-listing.tsx
vendored
|
@ -13,6 +13,7 @@ interface UserOther {
|
||||||
|
|
||||||
interface UserListingProps {
|
interface UserListingProps {
|
||||||
user: UserView | UserOther;
|
user: UserView | UserOther;
|
||||||
|
realLink?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserListing extends Component<UserListingProps, any> {
|
export class UserListing extends Component<UserListingProps, any> {
|
||||||
|
@ -29,8 +30,8 @@ export class UserListing extends Component<UserListingProps, any> {
|
||||||
name_ = user.name;
|
name_ = user.name;
|
||||||
link = `/u/${user.name}`;
|
link = `/u/${user.name}`;
|
||||||
} else {
|
} else {
|
||||||
name_ = `${hostname(user.actor_id)}/${user.name}`;
|
name_ = `${user.name}@${hostname(user.actor_id)}`;
|
||||||
link = `/user/${user.id}`;
|
link = !this.props.realLink ? `/user/${user.id}` : user.actor_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
2
ui/src/components/user.tsx
vendored
2
ui/src/components/user.tsx
vendored
|
@ -402,7 +402,7 @@ export class User extends Component<any, UserState> {
|
||||||
<h5>
|
<h5>
|
||||||
<ul class="list-inline mb-0">
|
<ul class="list-inline mb-0">
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<UserListing user={user} />
|
<UserListing user={user} realLink />
|
||||||
</li>
|
</li>
|
||||||
{user.banned && (
|
{user.banned && (
|
||||||
<li className="list-inline-item badge badge-danger">
|
<li className="list-inline-item badge badge-danger">
|
||||||
|
|
28
ui/src/utils.ts
vendored
28
ui/src/utils.ts
vendored
|
@ -501,7 +501,10 @@ export function setupTribute(): Tribute {
|
||||||
{
|
{
|
||||||
trigger: '@',
|
trigger: '@',
|
||||||
selectTemplate: (item: any) => {
|
selectTemplate: (item: any) => {
|
||||||
return `[/u/${item.original.key}](/u/${item.original.key})`;
|
let link = item.original.local
|
||||||
|
? `[@${item.original.key}](/u/${item.original.key})`
|
||||||
|
: `[@${item.original.key}](/user/${item.original.id})`;
|
||||||
|
return link;
|
||||||
},
|
},
|
||||||
values: (text: string, cb: any) => {
|
values: (text: string, cb: any) => {
|
||||||
userSearch(text, (users: any) => cb(users));
|
userSearch(text, (users: any) => cb(users));
|
||||||
|
@ -514,9 +517,12 @@ export function setupTribute(): Tribute {
|
||||||
|
|
||||||
// Communities
|
// Communities
|
||||||
{
|
{
|
||||||
trigger: '#',
|
trigger: '!',
|
||||||
selectTemplate: (item: any) => {
|
selectTemplate: (item: any) => {
|
||||||
return `[/c/${item.original.key}](/c/${item.original.key})`;
|
let link = item.original.local
|
||||||
|
? `[!${item.original.key}](/c/${item.original.key})`
|
||||||
|
: `[!${item.original.key}](/community/${item.original.id})`;
|
||||||
|
return link;
|
||||||
},
|
},
|
||||||
values: (text: string, cb: any) => {
|
values: (text: string, cb: any) => {
|
||||||
communitySearch(text, (communities: any) => cb(communities));
|
communitySearch(text, (communities: any) => cb(communities));
|
||||||
|
@ -559,7 +565,12 @@ function userSearch(text: string, cb: any) {
|
||||||
if (res.op == UserOperation.Search) {
|
if (res.op == UserOperation.Search) {
|
||||||
let data = res.data as SearchResponse;
|
let data = res.data as SearchResponse;
|
||||||
let users = data.users.map(u => {
|
let users = data.users.map(u => {
|
||||||
return { key: u.name };
|
let name_ = u.local ? u.name : `${u.name}@${hostname(u.actor_id)}`;
|
||||||
|
return {
|
||||||
|
key: name_,
|
||||||
|
local: u.local,
|
||||||
|
id: u.id,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
cb(users);
|
cb(users);
|
||||||
this.userSub.unsubscribe();
|
this.userSub.unsubscribe();
|
||||||
|
@ -590,8 +601,13 @@ function communitySearch(text: string, cb: any) {
|
||||||
let res = wsJsonToRes(msg);
|
let res = wsJsonToRes(msg);
|
||||||
if (res.op == UserOperation.Search) {
|
if (res.op == UserOperation.Search) {
|
||||||
let data = res.data as SearchResponse;
|
let data = res.data as SearchResponse;
|
||||||
let communities = data.communities.map(u => {
|
let communities = data.communities.map(c => {
|
||||||
return { key: u.name };
|
let name_ = c.local ? c.name : `${c.name}@${hostname(c.actor_id)}`;
|
||||||
|
return {
|
||||||
|
key: name_,
|
||||||
|
local: c.local,
|
||||||
|
id: c.id,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
cb(communities);
|
cb(communities);
|
||||||
this.communitySub.unsubscribe();
|
this.communitySub.unsubscribe();
|
||||||
|
|
Loading…
Reference in a new issue