Compare commits

...

2 commits

20 changed files with 2700 additions and 460 deletions

View file

@ -1,14 +1,22 @@
use crate::{ use crate::{
apub::{ apub::{
activities::send_activity_to_community, activities::send_activity_to_community,
create_apub_response, create_apub_tombstone_response, create_tombstone, create_apub_response,
create_apub_tombstone_response,
create_tombstone,
extensions::page_extension::PageExtension, extensions::page_extension::PageExtension,
fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user}, fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user},
ActorType, ApubLikeableType, ApubObjectType, FromApub, PageExt, ToApub, ActorType,
ApubLikeableType,
ApubObjectType,
FromApub,
PageExt,
ToApub,
}, },
blocking, blocking,
routes::DbPoolParam, routes::DbPoolParam,
DbPool, LemmyError, DbPool,
LemmyError,
}; };
use activitystreams_ext::Ext1; use activitystreams_ext::Ext1;
use activitystreams_new::{ use activitystreams_new::{

1
ui/package.json vendored
View file

@ -32,6 +32,7 @@
"husky": "^4.2.5", "husky": "^4.2.5",
"i18next": "^19.4.1", "i18next": "^19.4.1",
"inferno": "^7.4.2", "inferno": "^7.4.2",
"inferno-helmet": "^5.2.1",
"inferno-i18next": "nimbusec-oss/inferno-i18next", "inferno-i18next": "nimbusec-oss/inferno-i18next",
"inferno-router": "^7.4.2", "inferno-router": "^7.4.2",
"js-cookie": "^2.2.0", "js-cookie": "^2.2.0",

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -80,9 +81,18 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.siteRes.site.name) {
return `${i18n.t('admin_settings')} - ${this.state.siteRes.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
{this.state.loading ? ( {this.state.loading ? (
<h5> <h5>
<svg class="icon icon-spinner spin"> <svg class="icon icon-spinner spin">
@ -220,9 +230,6 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
} }
this.state.siteRes = data; this.state.siteRes = data;
this.setState(this.state); this.setState(this.state);
document.title = `${i18n.t('admin_settings')} - ${
this.state.siteRes.site.name
}`;
} else if (res.op == UserOperation.EditSite) { } else if (res.op == UserOperation.EditSite) {
let data = res.data as SiteResponse; let data = res.data as SiteResponse;
this.state.siteRes.site = data.site; this.state.siteRes.site = data.site;

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -11,6 +12,7 @@ import {
SortType, SortType,
WebSocketJsonResponse, WebSocketJsonResponse,
GetSiteResponse, GetSiteResponse,
Site,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { wsJsonToRes, toast, getPageFromProps } from '../utils'; import { wsJsonToRes, toast, getPageFromProps } from '../utils';
@ -25,6 +27,7 @@ interface CommunitiesState {
communities: Array<Community>; communities: Array<Community>;
page: number; page: number;
loading: boolean; loading: boolean;
site: Site;
} }
interface CommunitiesProps { interface CommunitiesProps {
@ -37,6 +40,7 @@ export class Communities extends Component<any, CommunitiesState> {
communities: [], communities: [],
loading: true, loading: true,
page: getPageFromProps(this.props), page: getPageFromProps(this.props),
site: undefined,
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -71,9 +75,18 @@ export class Communities extends Component<any, CommunitiesState> {
} }
} }
get documentTitle(): string {
if (this.state.site) {
return `${i18n.t('communities')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
{this.state.loading ? ( {this.state.loading ? (
<h5 class=""> <h5 class="">
<svg class="icon icon-spinner spin"> <svg class="icon icon-spinner spin">
@ -240,7 +253,8 @@ export class Communities extends Component<any, CommunitiesState> {
this.setState(this.state); this.setState(this.state);
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
document.title = `${i18n.t('communities')} - ${data.site.name}`; this.state.site = data.site;
this.setState(this.state);
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -174,9 +175,18 @@ export class Community extends Component<any, State> {
} }
} }
get documentTitle(): string {
if (this.state.community.name) {
return `/c/${this.state.community.name} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
{this.selects()} {this.selects()}
{this.state.loading ? ( {this.state.loading ? (
<h5> <h5>
@ -356,7 +366,6 @@ export class Community extends Component<any, State> {
this.state.community = data.community; this.state.community = data.community;
this.state.moderators = data.moderators; this.state.moderators = data.moderators;
this.state.online = data.online; this.state.online = data.online;
document.title = `/c/${this.state.community.name} - ${this.state.site.name}`;
this.setState(this.state); this.setState(this.state);
this.fetchData(); this.fetchData();
} else if ( } else if (

View file

@ -1,4 +1,5 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { CommunityForm } from './community-form'; import { CommunityForm } from './community-form';
@ -7,19 +8,33 @@ import {
UserOperation, UserOperation,
WebSocketJsonResponse, WebSocketJsonResponse,
GetSiteResponse, GetSiteResponse,
Site,
} from '../interfaces'; } from '../interfaces';
import { toast, wsJsonToRes } from '../utils'; import { toast, wsJsonToRes } from '../utils';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
interface CreateCommunityState { interface CreateCommunityState {
enableNsfw: boolean; site: Site;
} }
export class CreateCommunity extends Component<any, CreateCommunityState> { export class CreateCommunity extends Component<any, CreateCommunityState> {
private subscription: Subscription; private subscription: Subscription;
private emptyState: CreateCommunityState = { private emptyState: CreateCommunityState = {
enableNsfw: null, site: {
id: undefined,
name: undefined,
creator_id: undefined,
published: undefined,
creator_name: undefined,
number_of_users: undefined,
number_of_posts: undefined,
number_of_comments: undefined,
number_of_communities: undefined,
enable_downvotes: undefined,
open_registration: undefined,
enable_nsfw: undefined,
},
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
super(props, context); super(props, context);
@ -46,15 +61,24 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site.name) {
return `${i18n.t('create_community')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 col-lg-6 offset-lg-3 mb-4"> <div class="col-12 col-lg-6 offset-lg-3 mb-4">
<h5>{i18n.t('create_community')}</h5> <h5>{i18n.t('create_community')}</h5>
<CommunityForm <CommunityForm
onCreate={this.handleCommunityCreate} onCreate={this.handleCommunityCreate}
enableNsfw={this.state.enableNsfw} enableNsfw={this.state.site.enable_nsfw}
/> />
</div> </div>
</div> </div>
@ -74,9 +98,8 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
return; return;
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
this.state.enableNsfw = data.site.enable_nsfw; this.state.site = data.site;
this.setState(this.state); this.setState(this.state);
document.title = `${i18n.t('create_community')} - ${data.site.name}`;
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { PostForm } from './post-form'; import { PostForm } from './post-form';
@ -61,9 +62,18 @@ export class CreatePost extends Component<any, CreatePostState> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site.name) {
return `${i18n.t('create_post')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 col-lg-6 offset-lg-3 mb-4"> <div class="col-12 col-lg-6 offset-lg-3 mb-4">
<h5>{i18n.t('create_post')}</h5> <h5>{i18n.t('create_post')}</h5>
@ -117,7 +127,6 @@ export class CreatePost extends Component<any, CreatePostState> {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
this.state.site = data.site; this.state.site = data.site;
this.setState(this.state); this.setState(this.state);
document.title = `${i18n.t('create_post')} - ${data.site.name}`;
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { PrivateMessageForm } from './private-message-form'; import { PrivateMessageForm } from './private-message-form';
@ -7,15 +8,27 @@ import {
UserOperation, UserOperation,
WebSocketJsonResponse, WebSocketJsonResponse,
GetSiteResponse, GetSiteResponse,
Site,
PrivateMessageFormParams, PrivateMessageFormParams,
} from '../interfaces'; } from '../interfaces';
import { toast, wsJsonToRes } from '../utils'; import { toast, wsJsonToRes } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
export class CreatePrivateMessage extends Component<any, any> { interface CreatePrivateMessageState {
site: Site;
}
export class CreatePrivateMessage extends Component<
any,
CreatePrivateMessageState
> {
private subscription: Subscription; private subscription: Subscription;
private emptyState: CreatePrivateMessageState = {
site: undefined,
};
constructor(props: any, context: any) { constructor(props: any, context: any) {
super(props, context); super(props, context);
this.state = this.emptyState;
this.handlePrivateMessageCreate = this.handlePrivateMessageCreate.bind( this.handlePrivateMessageCreate = this.handlePrivateMessageCreate.bind(
this this
); );
@ -40,9 +53,18 @@ export class CreatePrivateMessage extends Component<any, any> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site) {
return `${i18n.t('create_private_message')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 col-lg-6 offset-lg-3 mb-4"> <div class="col-12 col-lg-6 offset-lg-3 mb-4">
<h5>{i18n.t('create_private_message')}</h5> <h5>{i18n.t('create_private_message')}</h5>
@ -80,9 +102,8 @@ export class CreatePrivateMessage extends Component<any, any> {
return; return;
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
document.title = `${i18n.t('create_private_message')} - ${ this.state.site = data.site;
data.site.name this.setState(this.state);
}`;
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -17,6 +18,7 @@ import {
PrivateMessagesResponse, PrivateMessagesResponse,
PrivateMessageResponse, PrivateMessageResponse,
GetSiteResponse, GetSiteResponse,
Site,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { import {
@ -57,7 +59,7 @@ interface InboxState {
messages: Array<PrivateMessageI>; messages: Array<PrivateMessageI>;
sort: SortType; sort: SortType;
page: number; page: number;
enableDownvotes: boolean; site: Site;
} }
export class Inbox extends Component<any, InboxState> { export class Inbox extends Component<any, InboxState> {
@ -70,7 +72,20 @@ export class Inbox extends Component<any, InboxState> {
messages: [], messages: [],
sort: SortType.New, sort: SortType.New,
page: 1, page: 1,
enableDownvotes: undefined, site: {
id: undefined,
name: undefined,
creator_id: undefined,
published: undefined,
creator_name: undefined,
number_of_users: undefined,
number_of_posts: undefined,
number_of_comments: undefined,
number_of_communities: undefined,
enable_downvotes: undefined,
open_registration: undefined,
enable_nsfw: undefined,
},
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -95,9 +110,20 @@ export class Inbox extends Component<any, InboxState> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site.name) {
return `/u/${UserService.Instance.user.name} ${i18n.t('inbox')} - ${
this.state.site.name
}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h5 class="mb-1"> <h5 class="mb-1">
@ -269,7 +295,7 @@ export class Inbox extends Component<any, InboxState> {
markable markable
showCommunity showCommunity
showContext showContext
enableDownvotes={this.state.enableDownvotes} enableDownvotes={this.state.site.enable_downvotes}
/> />
) : ( ) : (
<PrivateMessage privateMessage={i} /> <PrivateMessage privateMessage={i} />
@ -288,7 +314,7 @@ export class Inbox extends Component<any, InboxState> {
markable markable
showCommunity showCommunity
showContext showContext
enableDownvotes={this.state.enableDownvotes} enableDownvotes={this.state.site.enable_downvotes}
/> />
</div> </div>
); );
@ -304,7 +330,7 @@ export class Inbox extends Component<any, InboxState> {
markable markable
showCommunity showCommunity
showContext showContext
enableDownvotes={this.state.enableDownvotes} enableDownvotes={this.state.site.enable_downvotes}
/> />
))} ))}
</div> </div>
@ -557,11 +583,8 @@ export class Inbox extends Component<any, InboxState> {
this.setState(this.state); this.setState(this.state);
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
this.state.enableDownvotes = data.site.enable_downvotes; this.state.site = data.site;
this.setState(this.state); this.setState(this.state);
document.title = `/u/${UserService.Instance.user.name} ${i18n.t(
'inbox'
)} - ${data.site.name}`;
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -9,6 +10,7 @@ import {
PasswordResetForm, PasswordResetForm,
GetSiteResponse, GetSiteResponse,
WebSocketJsonResponse, WebSocketJsonResponse,
Site,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { wsJsonToRes, validEmail, toast } from '../utils'; import { wsJsonToRes, validEmail, toast } from '../utils';
@ -19,12 +21,12 @@ interface State {
registerForm: RegisterForm; registerForm: RegisterForm;
loginLoading: boolean; loginLoading: boolean;
registerLoading: boolean; registerLoading: boolean;
enable_nsfw: boolean;
mathQuestion: { mathQuestion: {
a: number; a: number;
b: number; b: number;
answer: number; answer: number;
}; };
site: Site;
} }
export class Login extends Component<any, State> { export class Login extends Component<any, State> {
@ -44,12 +46,25 @@ export class Login extends Component<any, State> {
}, },
loginLoading: false, loginLoading: false,
registerLoading: false, registerLoading: false,
enable_nsfw: undefined,
mathQuestion: { mathQuestion: {
a: Math.floor(Math.random() * 10) + 1, a: Math.floor(Math.random() * 10) + 1,
b: Math.floor(Math.random() * 10) + 1, b: Math.floor(Math.random() * 10) + 1,
answer: undefined, answer: undefined,
}, },
site: {
id: undefined,
name: undefined,
creator_id: undefined,
published: undefined,
creator_name: undefined,
number_of_users: undefined,
number_of_posts: undefined,
number_of_comments: undefined,
number_of_communities: undefined,
enable_downvotes: undefined,
open_registration: undefined,
enable_nsfw: undefined,
},
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -72,9 +87,18 @@ export class Login extends Component<any, State> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site.name) {
return `${i18n.t('login')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 col-lg-6 mb-4">{this.loginForm()}</div> <div class="col-12 col-lg-6 mb-4">{this.loginForm()}</div>
<div class="col-12 col-lg-6">{this.registerForm()}</div> <div class="col-12 col-lg-6">{this.registerForm()}</div>
@ -251,7 +275,7 @@ export class Login extends Component<any, State> {
/> />
</div> </div>
</div> </div>
{this.state.enable_nsfw && ( {this.state.site.enable_nsfw && (
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-10"> <div class="col-sm-10">
<div class="form-check"> <div class="form-check">
@ -392,9 +416,8 @@ export class Login extends Component<any, State> {
toast(i18n.t('reset_password_mail_sent')); toast(i18n.t('reset_password_mail_sent'));
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
this.state.enable_nsfw = data.site.enable_nsfw; this.state.site = data.site;
this.setState(this.state); this.setState(this.state);
document.title = `${i18n.t('login')} - ${data.site.name}`;
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
@ -177,9 +178,18 @@ export class Main extends Component<any, MainState> {
} }
} }
get documentTitle(): string {
if (this.state.siteRes.site.name) {
return `${this.state.siteRes.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<main role="main" class="col-12 col-md-8"> <main role="main" class="col-12 col-md-8">
{this.posts()} {this.posts()}
@ -627,7 +637,6 @@ export class Main extends Component<any, MainState> {
this.state.siteRes.banned = data.banned; this.state.siteRes.banned = data.banned;
this.state.siteRes.online = data.online; this.state.siteRes.online = data.online;
this.setState(this.state); this.setState(this.state);
document.title = `${this.state.siteRes.site.name}`;
} else if (res.op == UserOperation.EditSite) { } else if (res.op == UserOperation.EditSite) {
let data = res.data as SiteResponse; let data = res.data as SiteResponse;
this.state.siteRes.site = data.site; this.state.siteRes.site = data.site;

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
@ -17,6 +18,7 @@ import {
ModAdd, ModAdd,
WebSocketJsonResponse, WebSocketJsonResponse,
GetSiteResponse, GetSiteResponse,
Site,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { wsJsonToRes, addTypeInfo, fetchLimit, toast } from '../utils'; import { wsJsonToRes, addTypeInfo, fetchLimit, toast } from '../utils';
@ -38,6 +40,7 @@ interface ModlogState {
communityId?: number; communityId?: number;
communityName?: string; communityName?: string;
page: number; page: number;
site: Site;
loading: boolean; loading: boolean;
} }
@ -47,6 +50,7 @@ export class Modlog extends Component<any, ModlogState> {
combined: [], combined: [],
page: 1, page: 1,
loading: true, loading: true,
site: undefined,
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -338,9 +342,18 @@ export class Modlog extends Component<any, ModlogState> {
); );
} }
get documentTitle(): string {
if (this.state.site) {
return `Modlog - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
{this.state.loading ? ( {this.state.loading ? (
<h5 class=""> <h5 class="">
<svg class="icon icon-spinner spin"> <svg class="icon icon-spinner spin">
@ -434,7 +447,8 @@ export class Modlog extends Component<any, ModlogState> {
this.setCombined(data); this.setCombined(data);
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
document.title = `Modlog - ${data.site.name}`; this.state.site = data.site;
this.setState(this.state);
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -7,6 +8,7 @@ import {
PasswordChangeForm, PasswordChangeForm,
WebSocketJsonResponse, WebSocketJsonResponse,
GetSiteResponse, GetSiteResponse,
Site,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { wsJsonToRes, capitalizeFirstLetter, toast } from '../utils'; import { wsJsonToRes, capitalizeFirstLetter, toast } from '../utils';
@ -15,6 +17,7 @@ import { i18n } from '../i18next';
interface State { interface State {
passwordChangeForm: PasswordChangeForm; passwordChangeForm: PasswordChangeForm;
loading: boolean; loading: boolean;
site: Site;
} }
export class PasswordChange extends Component<any, State> { export class PasswordChange extends Component<any, State> {
@ -27,6 +30,7 @@ export class PasswordChange extends Component<any, State> {
password_verify: undefined, password_verify: undefined,
}, },
loading: false, loading: false,
site: undefined,
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -48,9 +52,18 @@ export class PasswordChange extends Component<any, State> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site) {
return `${i18n.t('password_change')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 col-lg-6 offset-lg-3 mb-4"> <div class="col-12 col-lg-6 offset-lg-3 mb-4">
<h5>{i18n.t('password_change')}</h5> <h5>{i18n.t('password_change')}</h5>
@ -142,7 +155,8 @@ export class PasswordChange extends Component<any, State> {
this.props.history.push('/'); this.props.history.push('/');
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
document.title = `${i18n.t('password_change')} - ${data.site.name}`; this.state.site = data.site;
this.setState(this.state);
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -180,9 +181,18 @@ export class Post extends Component<any, PostState> {
} }
} }
get documentTitle(): string {
if (this.state.post) {
return `${this.state.post.name} - ${this.state.siteRes.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
{this.state.loading ? ( {this.state.loading ? (
<h5> <h5>
<svg class="icon icon-spinner spin"> <svg class="icon icon-spinner spin">
@ -406,7 +416,6 @@ export class Post extends Component<any, PostState> {
this.state.moderators = data.moderators; this.state.moderators = data.moderators;
this.state.online = data.online; this.state.online = data.online;
this.state.loading = false; this.state.loading = false;
document.title = `${this.state.post.name} - ${this.state.siteRes.site.name}`;
// Get cross-posts // Get cross-posts
if (this.state.post.url) { if (this.state.post.url) {

View file

@ -1,5 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router'; import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -156,9 +156,24 @@ export class Search extends Component<any, SearchState> {
} }
} }
get documentTitle(): string {
if (this.state.site.name) {
if (this.state.q) {
return `${i18n.t('search')} - ${this.state.q} - ${
this.state.site.name
}`;
} else {
return `${i18n.t('search')} - ${this.state.site.name}`;
}
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<h5>{i18n.t('search')}</h5> <h5>{i18n.t('search')}</h5>
{this.selects()} {this.selects()}
{this.searchForm()} {this.searchForm()}
@ -500,9 +515,6 @@ export class Search extends Component<any, SearchState> {
let data = res.data as SearchResponse; let data = res.data as SearchResponse;
this.state.searchResponse = data; this.state.searchResponse = data;
this.state.loading = false; this.state.loading = false;
document.title = `${i18n.t('search')} - ${this.state.q} - ${
this.state.site.name
}`;
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (res.op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
@ -517,7 +529,6 @@ export class Search extends Component<any, SearchState> {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
this.state.site = data.site; this.state.site = data.site;
this.setState(this.state); this.setState(this.state);
document.title = `${i18n.t('search')} - ${data.site.name}`;
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { import {
@ -51,13 +52,14 @@ export class Setup extends Component<any, State> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
componentDidMount() { get documentTitle(): string {
document.title = `${i18n.t('setup')} - Lemmy`; return `${i18n.t('setup')} - Lemmy`;
} }
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 offset-lg-3 col-lg-6"> <div class="col-12 offset-lg-3 col-lg-6">
<h3>{i18n.t('lemmy_instance_setup')}</h3> <h3>{i18n.t('lemmy_instance_setup')}</h3>

View file

@ -1,9 +1,11 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { import {
GetSiteResponse, GetSiteResponse,
Site,
WebSocketJsonResponse, WebSocketJsonResponse,
UserOperation, UserOperation,
} from '../interfaces'; } from '../interfaces';
@ -42,10 +44,18 @@ let silver: Array<SilverUser> = [
// let gold = []; // let gold = [];
// let latinum = []; // let latinum = [];
export class Sponsors extends Component<any, any> { interface SponsorsState {
site: Site;
}
export class Sponsors extends Component<any, SponsorsState> {
private subscription: Subscription; private subscription: Subscription;
private emptyState: SponsorsState = {
site: undefined,
};
constructor(props: any, context: any) { constructor(props: any, context: any) {
super(props, context); super(props, context);
this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
.subscribe( .subscribe(
@ -65,9 +75,18 @@ export class Sponsors extends Component<any, any> {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
get documentTitle(): string {
if (this.state.site) {
return `${i18n.t('sponsors')} - ${this.state.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container text-center"> <div class="container text-center">
<Helmet title={this.documentTitle} />
{this.topMessage()} {this.topMessage()}
<hr /> <hr />
{this.sponsors()} {this.sponsors()}
@ -183,7 +202,8 @@ export class Sponsors extends Component<any, any> {
return; return;
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
document.title = `${i18n.t('sponsors')} - ${data.site.name}`; this.state.site = data.site;
this.setState(this.state);
} }
} }
} }

View file

@ -1,4 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Helmet } from 'inferno-helmet';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
@ -207,13 +208,21 @@ export class User extends Component<any, UserState> {
// Couldnt get a refresh working. This does for now. // Couldnt get a refresh working. This does for now.
location.reload(); location.reload();
} }
document.title = `/u/${this.state.username} - ${this.state.siteRes.site.name}`;
setupTippy(); setupTippy();
} }
get documentTitle(): string {
if (this.state.siteRes.site.name) {
return `/u/${this.state.username} - ${this.state.siteRes.site.name}`;
} else {
return 'Lemmy';
}
}
render() { render() {
return ( return (
<div class="container"> <div class="container">
<Helmet title={this.documentTitle} />
<div class="row"> <div class="row">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<h5> <h5>

View file

@ -34,7 +34,7 @@ export class UserService {
this.user = undefined; this.user = undefined;
Cookies.remove('jwt'); Cookies.remove('jwt');
setTheme(); setTheme();
this.jwtSub.next(undefined); this.jwtSub.next();
console.log('Logged out.'); console.log('Logged out.');
} }

2840
ui/yarn.lock vendored

File diff suppressed because it is too large Load diff