Merge branch 'LemmyNet:main' into userpage

This commit is contained in:
mahanstreamer 2021-10-28 20:50:04 -04:00 committed by GitHub
commit 0eb4649c19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 523 additions and 475 deletions

@ -1 +1 @@
Subproject commit 762cb699a98d11032fe924cf50bf09e252413f6e
Subproject commit 0d6ef6791f9175fb98ec99598c724d83e8e0d4ef

View file

@ -17,13 +17,13 @@
},
"repository": "https://github.com/LemmyNet/lemmy-ui",
"dependencies": {
"@typescript-eslint/parser": "^4.32.0",
"@typescript-eslint/parser": "^5.1.0",
"autosize": "^5.0.1",
"check-password-strength": "^2.0.3",
"choices.js": "^9.0.1",
"emoji-short-name": "^1.0.0",
"express": "~4.17.1",
"i18next": "^21.1.1",
"i18next": "^21.3.2",
"inferno": "^7.4.10",
"inferno-create-element": "^7.4.10",
"inferno-helmet": "^5.2.1",
@ -41,41 +41,41 @@
"moment": "^2.29.1",
"reconnecting-websocket": "^4.4.0",
"register-service-worker": "^1.7.2",
"rxjs": "^7.2.0",
"rxjs": "^7.4.0",
"serialize-javascript": "^6.0.0",
"tippy.js": "^6.3.1",
"toastify-js": "^1.11.1",
"tippy.js": "^6.3.2",
"toastify-js": "^1.11.2",
"tributejs": "^5.1.3",
"ws": "^8.2.2"
"ws": "^8.2.3"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/plugin-transform-runtime": "^7.14.5",
"@babel/plugin-transform-typescript": "^7.15.4",
"@babel/preset-env": "7.15.6",
"@babel/core": "^7.15.8",
"@babel/plugin-transform-runtime": "^7.15.8",
"@babel/plugin-transform-typescript": "^7.15.8",
"@babel/preset-env": "7.15.8",
"@babel/preset-typescript": "^7.14.5",
"@babel/runtime": "^7.15.4",
"@types/autosize": "^4.0.0",
"@types/express": "^4.17.13",
"@types/node": "^16.10.1",
"@types/node": "^16.11.1",
"@types/node-fetch": "^2.5.11",
"@types/serialize-javascript": "^5.0.1",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/eslint-plugin": "^5.1.0",
"babel-loader": "^8.2.2",
"babel-plugin-inferno": "^6.3.0",
"bootstrap": "^5.1.1",
"bootswatch": "^5.1.1",
"bootstrap": "^5.1.3",
"bootswatch": "^5.1.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^9.0.1",
"css-loader": "^6.3.0",
"eslint": "^7.30.0",
"css-loader": "^6.4.0",
"eslint": "^8.0.1",
"eslint-plugin-prettier": "^4.0.0",
"husky": "^7.0.2",
"import-sort-style-module": "^6.0.0",
"iso-639-1": "^2.1.9",
"lemmy-js-client": "0.13.1-rc.1",
"lint-staged": "^11.0.1",
"mini-css-extract-plugin": "^2.3.0",
"lemmy-js-client": "0.13.4-rc.1",
"lint-staged": "^11.2.3",
"mini-css-extract-plugin": "^2.4.2",
"node-fetch": "^2.6.1",
"node-sass": "^6.0.1",
"prettier": "^2.4.1",
@ -84,14 +84,14 @@
"prettier-plugin-packagejson": "^2.2.13",
"rimraf": "^3.0.2",
"run-node-webpack-plugin": "^1.3.0",
"sass-loader": "^12.1.0",
"sass-loader": "^12.2.0",
"sortpack": "^2.2.0",
"style-loader": "^3.3.0",
"terser": "^5.9.0",
"typescript": "^4.4.3",
"webpack": "5.54.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "4.3.0",
"typescript": "^4.4.4",
"webpack": "5.58.2",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "4.3.1",
"webpack-node-externals": "^3.0.0"
},
"engines": {

View file

@ -2,19 +2,12 @@ import { Component, createRef, linkEvent, RefObject } from "inferno";
import { Link } from "inferno-router";
import {
CommentResponse,
CommentView,
GetPersonMentions,
GetPersonMentionsResponse,
GetPrivateMessages,
GetReplies,
GetRepliesResponse,
GetReportCount,
GetReportCountResponse,
GetSiteResponse,
GetUnreadCount,
GetUnreadCountResponse,
PrivateMessageResponse,
PrivateMessagesResponse,
PrivateMessageView,
SortType,
UserOperation,
} from "lemmy-js-client";
import { Subscription } from "rxjs";
@ -23,7 +16,6 @@ import { UserService, WebSocketService } from "../../services";
import {
authField,
donateLemmyUrl,
fetchLimit,
getLanguage,
isBrowser,
notifyComment,
@ -47,9 +39,6 @@ interface NavbarProps {
interface NavbarState {
isLoggedIn: boolean;
expanded: boolean;
replies: CommentView[];
mentions: CommentView[];
messages: PrivateMessageView[];
unreadInboxCount: number;
unreadReportCount: number;
searchParam: string;
@ -68,9 +57,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
isLoggedIn: !!this.props.site_res.my_user,
unreadInboxCount: 0,
unreadReportCount: 0,
replies: [],
mentions: [],
messages: [],
expanded: false,
searchParam: "",
toggleSearch: false,
@ -577,30 +563,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
})
);
this.fetchUnreads();
} else if (op == UserOperation.GetReplies) {
let data = wsJsonToRes<GetRepliesResponse>(msg).data;
let unreadReplies = data.replies.filter(r => !r.comment.read);
this.state.replies = unreadReplies;
this.state.unreadInboxCount = this.calculateUnreadInboxCount();
this.setState(this.state);
this.sendUnreadCount();
} else if (op == UserOperation.GetPersonMentions) {
let data = wsJsonToRes<GetPersonMentionsResponse>(msg).data;
let unreadMentions = data.mentions.filter(r => !r.comment.read);
this.state.mentions = unreadMentions;
this.state.unreadInboxCount = this.calculateUnreadInboxCount();
this.setState(this.state);
this.sendUnreadCount();
} else if (op == UserOperation.GetPrivateMessages) {
let data = wsJsonToRes<PrivateMessagesResponse>(msg).data;
let unreadMessages = data.private_messages.filter(
r => !r.private_message.read
);
this.state.messages = unreadMessages;
this.state.unreadInboxCount = this.calculateUnreadInboxCount();
} else if (op == UserOperation.GetUnreadCount) {
let data = wsJsonToRes<GetUnreadCountResponse>(msg).data;
this.state.unreadInboxCount =
data.replies + data.mentions + data.private_messages;
this.setState(this.state);
this.sendUnreadCount();
} else if (op == UserOperation.GetReportCount) {
@ -628,7 +594,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
UserService.Instance.myUserInfo.local_user_view.local_user.id
)
) {
this.state.replies.push(data.comment_view);
this.state.unreadInboxCount++;
this.setState(this.state);
this.sendUnreadCount();
@ -643,7 +608,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
data.private_message_view.recipient.id ==
UserService.Instance.myUserInfo.local_user_view.person.id
) {
this.state.messages.push(data.private_message_view);
this.state.unreadInboxCount++;
this.setState(this.state);
this.sendUnreadCount();
@ -654,41 +618,13 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
}
fetchUnreads() {
// TODO we should just add a count call to the API for these, because this is a limited fetch,
// and it shouldn't have to fetch the actual content
if (this.currentLocation !== "/inbox") {
console.log("Fetching inbox unreads...");
let repliesForm: GetReplies = {
sort: SortType.New,
unread_only: true,
page: 1,
limit: fetchLimit,
auth: authField(),
};
console.log("Fetching inbox unreads...");
let personMentionsForm: GetPersonMentions = {
sort: SortType.New,
unread_only: true,
page: 1,
limit: fetchLimit,
auth: authField(),
};
let unreadForm: GetUnreadCount = {
auth: authField(),
};
let privateMessagesForm: GetPrivateMessages = {
unread_only: true,
page: 1,
limit: fetchLimit,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
WebSocketService.Instance.send(
wsClient.getPersonMentions(personMentionsForm)
);
WebSocketService.Instance.send(
wsClient.getPrivateMessages(privateMessagesForm)
);
}
WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
console.log("Fetching reports...");
@ -713,14 +649,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
);
}
calculateUnreadInboxCount(): number {
return (
this.state.replies.filter(r => !r.comment.read).length +
this.state.mentions.filter(r => !r.comment.read).length +
this.state.messages.filter(r => !r.private_message.read).length
);
}
get canAdmin(): boolean {
return (
UserService.Instance.myUserInfo &&

View file

@ -182,6 +182,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{i18n.t("creator")}
</div>
)}
{cv.creator.bot_account && (
<div className="badge badge-light d-none d-sm-inline mr-2">
{i18n.t("bot_account").toLowerCase()}
</div>
)}
{(cv.creator_banned_from_community || cv.creator.banned) && (
<div className="badge badge-danger mr-2">
{i18n.t("banned")}

View file

@ -23,13 +23,27 @@ export class MomentTime extends Component<MomentTimeProps, any> {
moment.locale(lang);
}
createdAndModifiedTimes() {
let created = this.props.data.published || this.props.data.when_;
return `
<div>
<div>
${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(created)}
</div>
<div>
${capitalizeFirstLetter(i18n.t("modified"))} ${this.format(
this.props.data.updated
)}
</div>
</div>`;
}
render() {
if (!this.props.ignoreUpdated && this.props.data.updated) {
return (
<span
data-tippy-content={`${capitalizeFirstLetter(
i18n.t("modified")
)} ${this.format(this.props.data.updated)}`}
data-tippy-content={this.createdAndModifiedTimes()}
data-tippy-allowHtml={true}
className="font-italics pointer unselectable"
>
<Icon icon="edit-2" classes="icon-inline mr-1" />
@ -37,13 +51,13 @@ export class MomentTime extends Component<MomentTimeProps, any> {
</span>
);
} else {
let str = this.props.data.published || this.props.data.when_;
let created = this.props.data.published || this.props.data.when_;
return (
<span
className="pointer unselectable"
data-tippy-content={this.format(str)}
data-tippy-content={this.format(created)}
>
{moment.utc(str).fromNow(!this.props.showAgo)}
{moment.utc(created).fromNow(!this.props.showAgo)}
</span>
);
}

View file

@ -31,7 +31,7 @@ import { Icon, Spinner } from "../common/icon";
const passwordStrengthOptions: Options<string> = [
{
id: 0,
value: "too_weak",
value: "very_weak",
minDiversity: 0,
minLength: 0,
},

View file

@ -533,11 +533,20 @@ export class Inbox extends Component<any, InboxState> {
i.state.replies = [];
i.state.mentions = [];
i.state.messages = [];
i.sendUnreadCount();
UserService.Instance.unreadInboxCountSub.next(0);
window.scrollTo(0, 0);
i.setState(i.state);
}
sendUnreadCount(read: boolean) {
let urcs = UserService.Instance.unreadInboxCountSub;
if (read) {
urcs.next(urcs.getValue() - 1);
} else {
urcs.next(urcs.getValue() + 1);
}
}
parseMessage(msg: any) {
let op = wsUserOp(msg);
console.log(msg);
@ -551,7 +560,6 @@ export class Inbox extends Component<any, InboxState> {
this.state.replies = data.replies;
this.state.combined = this.buildCombined();
this.state.loading = false;
this.sendUnreadCount();
window.scrollTo(0, 0);
this.setState(this.state);
setupTippy();
@ -559,7 +567,6 @@ export class Inbox extends Component<any, InboxState> {
let data = wsJsonToRes<GetPersonMentionsResponse>(msg).data;
this.state.mentions = data.mentions;
this.state.combined = this.buildCombined();
this.sendUnreadCount();
window.scrollTo(0, 0);
this.setState(this.state);
setupTippy();
@ -567,7 +574,6 @@ export class Inbox extends Component<any, InboxState> {
let data = wsJsonToRes<PrivateMessagesResponse>(msg).data;
this.state.messages = data.private_messages;
this.state.combined = this.buildCombined();
this.sendUnreadCount();
window.scrollTo(0, 0);
this.setState(this.state);
setupTippy();
@ -635,7 +641,7 @@ export class Inbox extends Component<any, InboxState> {
data.private_message_view.private_message.read;
}
}
this.sendUnreadCount();
this.sendUnreadCount(data.private_message_view.private_message.read);
this.setState(this.state);
} else if (op == UserOperation.MarkAllAsRead) {
// Moved to be instant
@ -671,7 +677,8 @@ export class Inbox extends Component<any, InboxState> {
found.comment.read = combinedView.comment.read =
data.comment_view.comment.read;
}
this.sendUnreadCount();
this.sendUnreadCount(data.comment_view.comment.read);
this.setState(this.state);
setupTippy();
} else if (op == UserOperation.MarkPersonMentionAsRead) {
@ -719,7 +726,7 @@ export class Inbox extends Component<any, InboxState> {
data.person_mention_view.person_mention.read;
}
}
this.sendUnreadCount();
this.sendUnreadCount(data.person_mention_view.person_mention.read);
this.setState(this.state);
} else if (op == UserOperation.CreateComment) {
let data = wsJsonToRes<CommentResponse>(msg).data;
@ -736,6 +743,37 @@ export class Inbox extends Component<any, InboxState> {
data.comment_view.creator.id ==
UserService.Instance.myUserInfo.local_user_view.person.id
) {
// If youre in the unread view, just remove it from the list
if (this.state.unreadOrAll == UnreadOrAll.Unread) {
this.state.replies = this.state.replies.filter(
r => r.comment.id !== data.comment_view.comment.parent_id
);
this.state.mentions = this.state.mentions.filter(
m => m.comment.id !== data.comment_view.comment.parent_id
);
this.state.combined = this.state.combined.filter(r => {
if (this.isMention(r.view))
return r.view.comment.id !== data.comment_view.comment.parent_id;
else return r.id !== data.comment_view.comment.parent_id;
});
} else {
let mention_found = this.state.mentions.find(
i => i.comment.id == data.comment_view.comment.parent_id
);
if (mention_found) {
mention_found.person_mention.read = true;
}
let reply_found = this.state.replies.find(
i => i.comment.id == data.comment_view.comment.parent_id
);
if (reply_found) {
reply_found.comment.read = true;
}
this.state.combined = this.buildCombined();
}
this.sendUnreadCount(true);
this.setState(this.state);
setupTippy();
// TODO this seems wrong, you should be using form_id
toast(i18n.t("reply_sent"));
}
@ -776,22 +814,7 @@ export class Inbox extends Component<any, InboxState> {
}
}
sendUnreadCount() {
UserService.Instance.unreadInboxCountSub.next(this.unreadCount());
}
unreadCount(): number {
return (
this.state.replies.filter(r => !r.comment.read).length +
this.state.mentions.filter(r => !r.person_mention.read).length +
this.state.messages.filter(
r =>
UserService.Instance.myUserInfo &&
!r.private_message.read &&
// TODO also seems very strange and wrong
r.creator.id !==
UserService.Instance.myUserInfo.local_user_view.person.id
).length
);
isMention(view: any): view is PersonMentionView {
return (view as PersonMentionView).person_mention !== undefined;
}
}

View file

@ -396,6 +396,16 @@ export class Profile extends Component<any, ProfileState> {
{i18n.t("banned")}
</li>
)}
{pv.person.admin && (
<li className="list-inline-item badge badge-light">
{i18n.t("admin")}
</li>
)}
{pv.person.bot_account && (
<li className="list-inline-item badge badge-light">
{i18n.t("bot_account").toLowerCase()}
</li>
)}
</ul>
</div>
<div className="flex-grow-1 unselectable pointer mx-2"></div>

View file

@ -288,6 +288,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{this.isAdmin && (
<span className="mx-1 badge badge-light">{i18n.t("admin")}</span>
)}
{post_view.creator.bot_account && (
<span className="mx-1 badge badge-light">
{i18n.t("bot_account").toLowerCase()}
</span>
)}
{(post_view.creator_banned_from_community ||
post_view.creator.banned) && (
<span className="mx-1 badge badge-danger">{i18n.t("banned")}</span>

View file

@ -13,18 +13,35 @@ interface PostListingsProps {
enableNsfw: boolean;
}
export class PostListings extends Component<PostListingsProps, any> {
private duplicatesMap = new Map<number, PostView[]>();
interface PostListingsState {
posts: PostView[];
}
export class PostListings extends Component<
PostListingsProps,
PostListingsState
> {
duplicatesMap = new Map<number, PostView[]>();
private emptyState: PostListingsState = {
posts: [],
};
constructor(props: any, context: any) {
super(props, context);
this.state = this.emptyState;
if (this.props.removeDuplicates) {
this.state.posts = this.removeDuplicates();
} else {
this.state.posts = this.props.posts;
}
}
render() {
return (
<div>
{this.props.posts.length > 0 ? (
this.outer().map(post_view => (
{this.state.posts.length > 0 ? (
this.state.posts.map(post_view => (
<>
<PostListing
post_view={post_view}
@ -50,16 +67,10 @@ export class PostListings extends Component<PostListingsProps, any> {
);
}
outer(): PostView[] {
let out = this.props.posts;
if (this.props.removeDuplicates) {
out = this.removeDuplicates(out);
}
removeDuplicates(): PostView[] {
// Must use a spread to clone the props, because splice will fail below otherwise.
let posts = [...this.props.posts];
return out;
}
removeDuplicates(posts: PostView[]): PostView[] {
// A map from post url to list of posts (dupes)
let urlMap = new Map<string, PostView[]>();

View file

@ -103,6 +103,7 @@ function format(value: any, format: any): any {
i18next.init({
debug: false,
compatibilityJSON: "v3",
// load: 'languageOnly',
// initImmediate: false,

697
yarn.lock

File diff suppressed because it is too large Load diff