);
// Create Http server with websocket support
- HttpServer::new(move || {
- let settings = Settings::get();
- App::new()
- .wrap(middleware::Logger::default())
- .data(pool.clone())
- .data(server.clone())
- // The routes
- .configure(api::config)
- .configure(federation::config)
- .configure(feeds::config)
- .configure(index::config)
- .configure(nodeinfo::config)
- .configure(webfinger::config)
- .configure(websocket::config)
- // static files
- .service(actix_files::Files::new(
- "/static",
- settings.front_end_dir.to_owned(),
- ))
- .service(actix_files::Files::new(
- "/docs",
- settings.front_end_dir + "/documentation",
- ))
- })
- .bind((settings.bind, settings.port))?
- .run()
- .await
+ Ok(
+ HttpServer::new(move || {
++ let settings = Settings::get();
+ App::new()
+ .wrap(middleware::Logger::default())
+ .data(pool.clone())
+ .data(server.clone())
+ // The routes
+ .configure(api::config)
+ .configure(federation::config)
+ .configure(feeds::config)
+ .configure(index::config)
+ .configure(nodeinfo::config)
+ .configure(webfinger::config)
+ .configure(websocket::config)
+ // static files
+ .service(actix_files::Files::new(
+ "/static",
+ settings.front_end_dir.to_owned(),
+ ))
+ .service(actix_files::Files::new(
+ "/docs",
+ settings.front_end_dir.to_owned() + "/documentation",
+ ))
+ })
+ .bind((settings.bind, settings.port))?
+ .run()
+ .await?,
+ )
}
pub front_end_dir: String,
pub rate_limit: RateLimitConfig,
pub email: Option<EmailConfig>,
- pub federation_enabled: bool,
+ pub federation: Federation,
}
- #[derive(Debug, Deserialize)]
+ #[derive(Debug, Deserialize, Clone)]
pub struct Setup {
pub admin_username: String,
pub admin_password: String,
pub pool_size: u32,
}
+#[derive(Debug, Deserialize)]
+pub struct Federation {
+ pub enabled: bool,
+ pub followed_instances: String,
+ pub tls_enabled: bool,
+}
+
lazy_static! {
- static ref SETTINGS: Settings = {
- match Settings::init() {
- Ok(c) => c,
- Err(e) => panic!("{}", e),
- }
- };
+ static ref SETTINGS: RwLock<Settings> = RwLock::new(match Settings::init() {
+ Ok(c) => c,
+ Err(e) => panic!("{}", e),
+ });
}
impl Settings {
},
"keywords": [],
"dependencies": {
+ "@joeattardi/emoji-button": "^2.12.1",
"@types/autosize": "^3.0.6",
- "@types/js-cookie": "^2.2.5",
+ "@types/js-cookie": "^2.2.6",
"@types/jwt-decode": "^2.2.1",
- "@types/markdown-it": "^10.0.0",
+ "@types/markdown-it": "^0.0.9",
"@types/markdown-it-container": "^2.0.2",
- "@types/node": "^13.9.2",
+ "@types/node": "^13.11.1",
"autosize": "^4.0.2",
"bootswatch": "^4.3.1",
- "classcat": "^1.1.3",
+ "classcat": "^4.0.2",
"dotenv": "^8.2.0",
"emoji-short-name": "^1.0.0",
- "husky": "^4.2.3",
- "i18next": "^19.3.3",
+ "husky": "^4.2.5",
+ "i18next": "^19.4.1",
"inferno": "^7.4.2",
"inferno-i18next": "nimbusec-oss/inferno-i18next",
"inferno-router": "^7.4.2",
</button>
{this.state.commentForm.content && (
<button
-- className={`btn btn-sm mr-2 btn-secondary ${this.state
-- .previewMode && 'active'}`}
++ className={`btn btn-sm mr-2 btn-secondary ${
++ this.state.previewMode && 'active'
++ }`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
}
>
<div
-- class={`${!this.props.noIndent &&
++ class={`${
++ !this.props.noIndent &&
this.props.node.comment.parent_id &&
-- 'ml-2'}`}
++ 'ml-2'
++ }`}
>
<div class="d-flex flex-wrap align-items-center mb-1 mt-1 text-muted small">
- <Link
- className="mr-2 text-body font-weight-bold"
- to={`/u/${node.comment.creator_name}`}
- >
- {node.comment.creator_avatar && showAvatars() && (
- <img
- height="32"
- width="32"
- src={pictshareAvatarThumbnail(node.comment.creator_avatar)}
- class="rounded-circle mr-1"
- />
- )}
- <span>{node.comment.creator_name}</span>
- </Link>
+ <span class="mr-2">
+ <UserListing
+ user={{
+ name: node.comment.creator_name,
+ avatar: node.comment.creator_avatar,
+ }}
+ />
+ </span>
{this.isMod && (
<div className="badge badge-light d-none d-sm-inline mr-2">
{i18n.t('mod')}
this.loadingIcon
) : (
<svg
-- class={`icon icon-inline ${node.comment.read &&
-- 'text-success'}`}
++ class={`icon icon-inline ${
++ node.comment.read && 'text-success'
++ }`}
>
<use xlinkHref="#icon-check"></use>
</svg>
this.loadingIcon
) : (
<svg
-- class={`icon icon-inline ${node.comment.saved &&
-- 'text-warning'}`}
++ class={`icon icon-inline ${
++ node.comment.saved && 'text-warning'
++ }`}
>
<use xlinkHref="#icon-star"></use>
</svg>
data-tippy-content={i18n.t('view_source')}
>
<svg
-- class={`icon icon-inline ${this.state
-- .viewSource && 'text-success'}`}
++ class={`icon icon-inline ${
++ this.state.viewSource && 'text-success'
++ }`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>
}
>
<svg
-- class={`icon icon-inline ${node.comment
-- .deleted && 'text-danger'}`}
++ class={`icon icon-inline ${
++ node.comment.deleted && 'text-danger'
++ }`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
);
}
+ get canAdmin(): boolean {
+ return (
+ UserService.Instance.user &&
+ this.state.admins.map(a => a.id).includes(UserService.Instance.user.id)
+ );
+ }
+
requestNotificationPermission() {
if (UserService.Instance.user) {
-- document.addEventListener('DOMContentLoaded', function() {
++ document.addEventListener('DOMContentLoaded', function () {
if (!Notification) {
toast(i18n.t('notifications_error'), 'danger');
return;
<form>
<label
htmlFor="file-upload"
-- className={`${UserService.Instance.user &&
- 'pointer'} d-inline-block float-right text-muted h6 font-weight-bold`}
- 'pointer'} d-inline-block float-right text-muted font-weight-bold`}
++ className={`${
++ UserService.Instance.user && 'pointer'
++ } d-inline-block float-right text-muted font-weight-bold`}
data-tippy-content={i18n.t('upload_image')}
>
<svg class="icon icon-inline">
)}
{this.state.postForm.body && (
<button
-- className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state
-- .previewMode && 'active'}`}
++ className={`mt-1 mr-2 btn btn-sm btn-secondary ${
++ this.state.previewMode && 'active'
++ }`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
isImage,
isVideo,
getUnixTime,
- pictshareAvatarThumbnail,
- showAvatars,
pictshareImage,
setupTippy,
+ hostname,
+ previewLines,
} from '../utils';
import { i18n } from '../i18next';
return (
<div class="btn-group btn-group-toggle mb-2">
<label
-- className={`btn btn-sm btn-secondary pointer ${this.state
-- .commentSort === CommentSortType.Hot && 'active'}`}
++ className={`btn btn-sm btn-secondary pointer ${
++ this.state.commentSort === CommentSortType.Hot && 'active'
++ }`}
>
{i18n.t('hot')}
<input
/>
</label>
<label
-- className={`btn btn-sm btn-secondary pointer ${this.state
-- .commentSort === CommentSortType.Top && 'active'}`}
++ className={`btn btn-sm btn-secondary pointer ${
++ this.state.commentSort === CommentSortType.Top && 'active'
++ }`}
>
{i18n.t('top')}
<input
/>
</label>
<label
-- className={`btn btn-sm btn-secondary pointer ${this.state
-- .commentSort === CommentSortType.New && 'active'}`}
++ className={`btn btn-sm btn-secondary pointer ${
++ this.state.commentSort === CommentSortType.New && 'active'
++ }`}
>
{i18n.t('new')}
<input
/>
</label>
<label
-- className={`btn btn-sm btn-secondary pointer ${this.state
-- .commentSort === CommentSortType.Old && 'active'}`}
++ className={`btn btn-sm btn-secondary pointer ${
++ this.state.commentSort === CommentSortType.Old && 'active'
++ }`}
>
{i18n.t('old')}
<input
</button>
{this.state.privateMessageForm.content && (
<button
-- className={`btn btn-secondary mr-2 ${this.state.previewMode &&
-- 'active'}`}
++ className={`btn btn-secondary mr-2 ${
++ this.state.previewMode && 'active'
++ }`}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{i18n.t('preview')}
}
>
<svg
-- class={`icon icon-inline ${message.read &&
-- 'text-success'}`}
++ class={`icon icon-inline ${
++ message.read && 'text-success'
++ }`}
>
<use xlinkHref="#icon-check"></use>
</svg>
}
>
<svg
-- class={`icon icon-inline ${message.deleted &&
-- 'text-danger'}`}
++ class={`icon icon-inline ${
++ message.deleted && 'text-danger'
++ }`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
data-tippy-content={i18n.t('view_source')}
>
<svg
-- class={`icon icon-inline ${this.state.viewSource &&
-- 'text-success'}`}
++ class={`icon icon-inline ${
++ this.state.viewSource && 'text-success'
++ }`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>
}
>
<svg
-- class={`icon icon-inline ${community.deleted &&
-- 'text-danger'}`}
++ class={`icon icon-inline ${
++ community.deleted && 'text-danger'
++ }`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
))}
</ul>
<Link
-- class={`btn btn-sm btn-secondary btn-block mb-3 ${(community.deleted ||
-- community.removed) &&
-- 'no-click'}`}
++ class={`btn btn-sm btn-secondary btn-block mb-3 ${
++ (community.deleted || community.removed) && 'no-click'
++ }`}
to={`/create_post?community=${community.name}`}
>
{i18n.t('create_a_post')}
return `hsla(${Math.random() * 360}, 100%, 50%, 1)`;
}
+ export function previewLines(text: string, lines: number = 3): string {
+ // Use lines * 2 because markdown requires 2 lines
+ return text
+ .split('\n')
+ .slice(0, lines * 2)
+ .join('\n');
+ }
++
+export function hostname(url: string): string {
+ return new URL(url).hostname;
+}
dependencies:
"@types/linkify-it" "*"
- "@types/node@^13.9.2":
- version "13.9.2"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349"
- integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg==
-"@types/markdown-it@^10.0.0":
- version "10.0.0"
- resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-10.0.0.tgz#a2b5f9fb444bb27c1e0c4a0116fea09b3c6ebc1e"
- integrity sha512-7UPBg1W0rfsqQ1JwNFfhxibKO0t7Q0scNt96XcFIFLGE/vhZamzZayaFS2LKha/26Pz7b/2GgiaxQZ1GUwW0dA==
- dependencies:
- "@types/linkify-it" "*"
- "@types/mdurl" "*"
-
-"@types/mdurl@*":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
- integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
-
+ "@types/node@^13.11.1":
+ version "13.11.1"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7"
+ integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"