Front end federation names and links for users, posts, and communities.

This commit is contained in:
Dessalines 2020-04-14 19:18:13 -04:00
parent 1336b4ed60
commit fcf1c65fc1
16 changed files with 138 additions and 54 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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: [],

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View file

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