Front end federation names and links for users, posts, and communities.
This commit is contained in:
parent
1336b4ed60
commit
fcf1c65fc1
16 changed files with 138 additions and 54 deletions
|
@ -1,9 +1,8 @@
|
|||
use super::*;
|
||||
use crate::apub::activities::follow_community;
|
||||
use crate::apub::{format_community_name, gen_keypair_str, make_apub_endpoint, EndpointType};
|
||||
use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType};
|
||||
use diesel::PgConnection;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GetCommunity {
|
||||
|
@ -144,7 +143,7 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
|
|||
}
|
||||
};
|
||||
|
||||
let mut community_view = match CommunityView::read(&conn, community.id, user_id) {
|
||||
let community_view = match CommunityView::read(&conn, community.id, user_id) {
|
||||
Ok(community) => community,
|
||||
Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
|
||||
};
|
||||
|
@ -160,12 +159,6 @@ impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
|
|||
let creator_user = admins.remove(creator_index);
|
||||
admins.insert(0, creator_user);
|
||||
|
||||
if !community.local {
|
||||
let domain = Url::parse(&community.actor_id)?;
|
||||
community_view.name =
|
||||
format_community_name(&community_view.name.to_string(), domain.host_str().unwrap());
|
||||
}
|
||||
|
||||
// Return the jwt
|
||||
Ok(GetCommunityResponse {
|
||||
community: community_view,
|
||||
|
|
|
@ -92,16 +92,6 @@ fn vec_bytes_to_str(bytes: Vec<u8>) -> String {
|
|||
String::from_utf8_lossy(&bytes).into_owned()
|
||||
}
|
||||
|
||||
/// If community is on local instance, don't include the @instance part. This is only for displaying
|
||||
/// to the user and should never be used otherwise.
|
||||
pub fn format_community_name(name: &str, instance: &str) -> String {
|
||||
if instance == Settings::get().hostname {
|
||||
format!("!{}", name)
|
||||
} else {
|
||||
format!("!{}@{}", name, instance)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_following_instances() -> Vec<Instance> {
|
||||
Settings::get()
|
||||
.federation
|
||||
|
|
|
@ -60,7 +60,7 @@ pub struct Database {
|
|||
pub pool_size: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Federation {
|
||||
pub enabled: bool,
|
||||
pub followed_instances: String,
|
||||
|
|
6
ui/src/components/admin-settings.tsx
vendored
6
ui/src/components/admin-settings.tsx
vendored
|
@ -113,6 +113,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
|||
user={{
|
||||
name: admin.name,
|
||||
avatar: admin.avatar,
|
||||
id: admin.id,
|
||||
local: admin.local,
|
||||
actor_id: admin.actor_id,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
|
@ -133,6 +136,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
|||
user={{
|
||||
name: banned.name,
|
||||
avatar: banned.avatar,
|
||||
id: banned.id,
|
||||
local: banned.local,
|
||||
actor_id: banned.actor_id,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
|
|
3
ui/src/components/comment-node.tsx
vendored
3
ui/src/components/comment-node.tsx
vendored
|
@ -154,6 +154,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
|||
user={{
|
||||
name: node.comment.creator_name,
|
||||
avatar: node.comment.creator_avatar,
|
||||
id: node.comment.creator_id,
|
||||
local: node.comment.creator_local,
|
||||
actor_id: node.comment.creator_actor_id,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
|
|
5
ui/src/components/communities.tsx
vendored
5
ui/src/components/communities.tsx
vendored
|
@ -14,6 +14,7 @@ import {
|
|||
} from '../interfaces';
|
||||
import { WebSocketService } from '../services';
|
||||
import { wsJsonToRes, toast } from '../utils';
|
||||
import { CommunityLink } from './community-link';
|
||||
import { i18n } from '../i18next';
|
||||
|
||||
declare const Sortable: any;
|
||||
|
@ -104,9 +105,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
{this.state.communities.map(community => (
|
||||
<tr>
|
||||
<td>
|
||||
<Link to={`/c/${community.name}`}>
|
||||
{community.name}
|
||||
</Link>
|
||||
<CommunityLink community={community} />
|
||||
</td>
|
||||
<td class="d-none d-lg-table-cell">{community.title}</td>
|
||||
<td>{community.category_name}</td>
|
||||
|
|
35
ui/src/components/community-link.tsx
vendored
Normal file
35
ui/src/components/community-link.tsx
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { Component } from 'inferno';
|
||||
import { Link } from 'inferno-router';
|
||||
import { Community } from '../interfaces';
|
||||
import { hostname } from '../utils';
|
||||
|
||||
interface CommunityOther {
|
||||
name: string;
|
||||
id?: number; // Necessary if its federated
|
||||
local?: boolean;
|
||||
actor_id?: string;
|
||||
}
|
||||
|
||||
interface CommunityLinkProps {
|
||||
community: Community | CommunityOther;
|
||||
}
|
||||
|
||||
export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
render() {
|
||||
let community = this.props.community;
|
||||
let name_: string, link: string;
|
||||
let local = community.local == null ? true : community.local;
|
||||
if (local) {
|
||||
name_ = community.name;
|
||||
link = `/c/${community.name}`;
|
||||
} else {
|
||||
name_ = `${hostname(community.actor_id)}/${community.name}`;
|
||||
link = `/community/${community.id}`;
|
||||
}
|
||||
return <Link to={link}>{name_}</Link>;
|
||||
}
|
||||
}
|
5
ui/src/components/community.tsx
vendored
5
ui/src/components/community.tsx
vendored
|
@ -80,6 +80,11 @@ export class Community extends Component<any, State> {
|
|||
removed: null,
|
||||
nsfw: false,
|
||||
deleted: null,
|
||||
local: null,
|
||||
actor_id: null,
|
||||
last_refreshed_at: null,
|
||||
creator_actor_id: null,
|
||||
creator_local: null,
|
||||
},
|
||||
moderators: [],
|
||||
admins: [],
|
||||
|
|
17
ui/src/components/main.tsx
vendored
17
ui/src/components/main.tsx
vendored
|
@ -34,6 +34,7 @@ import { ListingTypeSelect } from './listing-type-select';
|
|||
import { DataTypeSelect } from './data-type-select';
|
||||
import { SiteForm } from './site-form';
|
||||
import { UserListing } from './user-listing';
|
||||
import { CommunityLink } from './community-link';
|
||||
import {
|
||||
wsJsonToRes,
|
||||
repoUrl,
|
||||
|
@ -190,9 +191,14 @@ export class Main extends Component<any, MainState> {
|
|||
<ul class="list-inline">
|
||||
{this.state.subscribedCommunities.map(community => (
|
||||
<li class="list-inline-item">
|
||||
<Link to={`/c/${community.community_name}`}>
|
||||
{community.community_name}
|
||||
</Link>
|
||||
<CommunityLink
|
||||
community={{
|
||||
name: community.community_name,
|
||||
id: community.community_id,
|
||||
local: community.community_local,
|
||||
actor_id: community.community_actor_id,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -228,7 +234,7 @@ export class Main extends Component<any, MainState> {
|
|||
<ul class="list-inline">
|
||||
{this.state.trendingCommunities.map(community => (
|
||||
<li class="list-inline-item">
|
||||
<Link to={`/c/${community.name}`}>{community.name}</Link>
|
||||
<CommunityLink community={community} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -319,6 +325,9 @@ export class Main extends Component<any, MainState> {
|
|||
user={{
|
||||
name: admin.name,
|
||||
avatar: admin.avatar,
|
||||
local: admin.local,
|
||||
actor_id: admin.actor_id,
|
||||
id: admin.id,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
|
|
7
ui/src/components/post-form.tsx
vendored
7
ui/src/components/post-form.tsx
vendored
|
@ -35,6 +35,7 @@ import {
|
|||
setupTribute,
|
||||
setupTippy,
|
||||
emojiPicker,
|
||||
hostname,
|
||||
} from '../utils';
|
||||
import autosize from 'autosize';
|
||||
import Tribute from 'tributejs/src/Tribute.js';
|
||||
|
@ -331,7 +332,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
|||
onInput={linkEvent(this, this.handlePostCommunityChange)}
|
||||
>
|
||||
{this.state.communities.map(community => (
|
||||
<option value={community.id}>{community.name}</option>
|
||||
<option value={community.id}>
|
||||
{community.local
|
||||
? community.name
|
||||
: `${hostname(community.actor_id)}/${community.name}`}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
|
21
ui/src/components/post-listing.tsx
vendored
21
ui/src/components/post-listing.tsx
vendored
|
@ -20,6 +20,7 @@ import { MomentTime } from './moment-time';
|
|||
import { PostForm } from './post-form';
|
||||
import { IFramelyCard } from './iframely-card';
|
||||
import { UserListing } from './user-listing';
|
||||
import { CommunityLink } from './community-link';
|
||||
import {
|
||||
md,
|
||||
mdToHtml,
|
||||
|
@ -420,6 +421,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
user={{
|
||||
name: post.creator_name,
|
||||
avatar: post.creator_avatar,
|
||||
id: post.creator_id,
|
||||
local: post.creator_local,
|
||||
actor_id: post.creator_actor_id,
|
||||
}}
|
||||
/>
|
||||
{this.isMod && (
|
||||
|
@ -440,15 +444,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
{this.props.showCommunity && (
|
||||
<span>
|
||||
<span> {i18n.t('to')} </span>
|
||||
{post.local ? (
|
||||
<Link to={`/c/${post.community_name}`}>
|
||||
{post.community_name}
|
||||
</Link>
|
||||
) : (
|
||||
<a href={post.community_actor_id} target="_blank">
|
||||
{hostname(post.ap_id)}/{post.community_name}
|
||||
</a>
|
||||
)}
|
||||
<CommunityLink
|
||||
community={{
|
||||
name: post.community_name,
|
||||
id: post.community_id,
|
||||
local: post.community_local,
|
||||
actor_id: post.community_actor_id,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
|
|
3
ui/src/components/private-message-form.tsx
vendored
3
ui/src/components/private-message-form.tsx
vendored
|
@ -135,6 +135,9 @@ export class PrivateMessageForm extends Component<
|
|||
user={{
|
||||
name: this.state.recipient.name,
|
||||
avatar: this.state.recipient.avatar,
|
||||
id: this.state.recipient.id,
|
||||
local: this.state.recipient.local,
|
||||
actor_id: this.state.recipient.actor_id,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
32
ui/src/components/sidebar.tsx
vendored
32
ui/src/components/sidebar.tsx
vendored
|
@ -8,12 +8,7 @@ import {
|
|||
UserView,
|
||||
} from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
import {
|
||||
mdToHtml,
|
||||
getUnixTime,
|
||||
pictshareAvatarThumbnail,
|
||||
showAvatars,
|
||||
} from '../utils';
|
||||
import { mdToHtml, getUnixTime, hostname } from '../utils';
|
||||
import { CommunityForm } from './community-form';
|
||||
import { UserListing } from './user-listing';
|
||||
import { i18n } from '../i18next';
|
||||
|
@ -65,6 +60,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
|
||||
sidebar() {
|
||||
let community = this.props.community;
|
||||
let name_: string, link: string;
|
||||
|
||||
if (community.local) {
|
||||
name_ = community.name;
|
||||
link = `/c/${community.name}`;
|
||||
} else {
|
||||
name_ = `${hostname(community.actor_id)}/${community.name}`;
|
||||
link = community.actor_id;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div class="card border-secondary mb-3">
|
||||
|
@ -82,9 +86,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
</small>
|
||||
)}
|
||||
</h5>
|
||||
<Link className="text-muted" to={`/c/${community.name}`}>
|
||||
/c/{community.name}
|
||||
</Link>
|
||||
{community.local ? (
|
||||
<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">
|
||||
{this.canMod && (
|
||||
<>
|
||||
|
@ -210,11 +220,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
user={{
|
||||
name: mod.user_name,
|
||||
avatar: mod.avatar,
|
||||
id: mod.user_id,
|
||||
local: mod.user_local,
|
||||
actor_id: mod.user_actor_id,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{/* TODO the to= needs to be able to handle community_ids as well, since they're federated */}
|
||||
<Link
|
||||
class={`btn btn-sm btn-secondary btn-block mb-3 ${
|
||||
(community.deleted || community.removed) && 'no-click'
|
||||
|
|
20
ui/src/components/user-listing.tsx
vendored
20
ui/src/components/user-listing.tsx
vendored
|
@ -1,11 +1,14 @@
|
|||
import { Component } from 'inferno';
|
||||
import { Link } from 'inferno-router';
|
||||
import { UserView } from '../interfaces';
|
||||
import { pictshareAvatarThumbnail, showAvatars } from '../utils';
|
||||
import { pictshareAvatarThumbnail, showAvatars, hostname } from '../utils';
|
||||
|
||||
interface UserOther {
|
||||
name: string;
|
||||
id?: number; // Necessary if its federated
|
||||
avatar?: string;
|
||||
local?: boolean;
|
||||
actor_id?: string;
|
||||
}
|
||||
|
||||
interface UserListingProps {
|
||||
|
@ -19,8 +22,19 @@ export class UserListing extends Component<UserListingProps, any> {
|
|||
|
||||
render() {
|
||||
let user = this.props.user;
|
||||
let local = user.local == null ? true : user.local;
|
||||
let name_: string, link: string;
|
||||
|
||||
if (local) {
|
||||
name_ = user.name;
|
||||
link = `/u/${user.name}`;
|
||||
} else {
|
||||
name_ = `${hostname(user.actor_id)}/${user.name}`;
|
||||
link = `/user/${user.id}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link className="text-body font-weight-bold" to={`/u/${user.name}`}>
|
||||
<Link className="text-body font-weight-bold" to={link}>
|
||||
{user.avatar && showAvatars() && (
|
||||
<img
|
||||
height="32"
|
||||
|
@ -29,7 +43,7 @@ export class UserListing extends Component<UserListingProps, any> {
|
|||
class="rounded-circle mr-2"
|
||||
/>
|
||||
)}
|
||||
<span>{user.name}</span>
|
||||
<span>{name_}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
|
13
ui/src/components/user.tsx
vendored
13
ui/src/components/user.tsx
vendored
|
@ -40,6 +40,7 @@ import {
|
|||
setupTippy,
|
||||
} from '../utils';
|
||||
import { PostListing } from './post-listing';
|
||||
import { UserListing } from './user-listing';
|
||||
import { SortSelect } from './sort-select';
|
||||
import { ListingTypeSelect } from './listing-type-select';
|
||||
import { CommentNodes } from './comment-nodes';
|
||||
|
@ -81,7 +82,6 @@ export class User extends Component<any, UserState> {
|
|||
user: {
|
||||
id: null,
|
||||
name: null,
|
||||
fedi_name: null,
|
||||
published: null,
|
||||
number_of_posts: null,
|
||||
post_score: null,
|
||||
|
@ -91,6 +91,8 @@ export class User extends Component<any, UserState> {
|
|||
avatar: null,
|
||||
show_avatars: null,
|
||||
send_notifications_to_email: null,
|
||||
actor_id: null,
|
||||
local: null,
|
||||
},
|
||||
user_id: null,
|
||||
username: null,
|
||||
|
@ -399,7 +401,9 @@ export class User extends Component<any, UserState> {
|
|||
<div class="card-body">
|
||||
<h5>
|
||||
<ul class="list-inline mb-0">
|
||||
<li className="list-inline-item">{user.name}</li>
|
||||
<li className="list-inline-item">
|
||||
<UserListing user={user} />
|
||||
</li>
|
||||
{user.banned && (
|
||||
<li className="list-inline-item badge badge-danger">
|
||||
{i18n.t('banned')}
|
||||
|
@ -455,8 +459,9 @@ export class User extends Component<any, UserState> {
|
|||
) : (
|
||||
<>
|
||||
<a
|
||||
className={`btn btn-block btn-secondary mt-3 ${!this.state
|
||||
.user.matrix_user_id && 'disabled'}`}
|
||||
className={`btn btn-block btn-secondary mt-3 ${
|
||||
!this.state.user.matrix_user_id && 'disabled'
|
||||
}`}
|
||||
target="_blank"
|
||||
href={`https://matrix.to/#/${this.state.user.matrix_user_id}`}
|
||||
>
|
||||
|
|
2
ui/src/env.ts
vendored
2
ui/src/env.ts
vendored
|
@ -1,6 +1,6 @@
|
|||
const host = `${window.location.hostname}`;
|
||||
const port = `${
|
||||
window.location.port == '4444' ? '8536' : window.location.port
|
||||
window.location.port == '4444' ? '8540' : window.location.port
|
||||
}`;
|
||||
const endpoint = `${host}:${port}`;
|
||||
|
||||
|
|
Loading…
Reference in a new issue