From 927c9c06a15aadfab682b07898ce2c0f8ce5d517 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Tue, 3 Dec 2019 15:33:10 -0500 Subject: [PATCH 01/35] Bump Hyperspace package version --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 28bc836..87bce95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hyperspace", - "version": "1.1.0-beta1", + "version": "1.1.0-beta2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f1b3b37..1354618 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hyperspace", "productName": "Hyperspace Desktop", - "version": "1.1.0-beta1", + "version": "1.1.0-beta2", "description": "A beautiful, fluffy client for the fediverse", "author": "Marquis Kurt ", "repository": "https://github.com/hyperspacedev/hyperspace.git", From e768a24e94ba8307adb8f57b2cda39cd4dea1d32 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Tue, 3 Dec 2019 15:46:17 -0500 Subject: [PATCH 02/35] Rename methods, run uplaod event only if media list is not empty --- src/pages/Compose.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/Compose.tsx b/src/pages/Compose.tsx index fa8915f..0cee323 100644 --- a/src/pages/Compose.tsx +++ b/src/pages/Compose.tsx @@ -121,7 +121,10 @@ class Composer extends Component { } } } - this.actuallyUploadMedia(fileList); + + if (fileList.length > 0) { + this.uploadMedia(fileList); + } } } }); @@ -190,12 +193,12 @@ class Composer extends Component { this.setState({ visibility }); } - uploadMedia() { + promptMediaDialog() { filedialog({ multiple: false, accept: ".jpeg,.jpg,.png,.gif,.webm,.mp4,.mov,.ogg,.wav,.mp3,.flac" }) - .then((media: FileList) => this.actuallyUploadMedia(media)) + .then((media: FileList) => this.uploadMedia(media)) .catch((err: Error) => { this.props.enqueueSnackbar("Couldn't get media: " + err.name, { variant: "error" @@ -204,7 +207,7 @@ class Composer extends Component { }); } - actuallyUploadMedia(media: FileList | File[]) { + uploadMedia(media: FileList | File[]) { let mediaForm = new FormData(); mediaForm.append("file", media[0]); this.props.enqueueSnackbar("Uploading media...", { @@ -652,7 +655,7 @@ class Composer extends Component { this.uploadMedia()} + onClick={() => this.promptMediaDialog()} id="compose-media" > From b4496720a38d62b84b8b20e837c5a9fddaff6c21 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Tue, 3 Dec 2019 15:53:11 -0500 Subject: [PATCH 03/35] Add getContrastText to titleBarText class --- src/components/AppLayout/AppLayout.styles.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/AppLayout/AppLayout.styles.tsx b/src/components/AppLayout/AppLayout.styles.tsx index 8c9824d..5222cd6 100644 --- a/src/components/AppLayout/AppLayout.styles.tsx +++ b/src/components/AppLayout/AppLayout.styles.tsx @@ -35,7 +35,8 @@ export const styles = (theme: Theme) => titleBarText: { fontSize: 12, paddingTop: 2, - paddingBottom: 1 + paddingBottom: 1, + color: theme.palette.getContrastText(theme.palette.primary.main) }, appBar: { zIndex: 1000, From 6c8035465ad87791f5801f1545a7fcaa24154f5e Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Tue, 3 Dec 2019 16:19:38 -0500 Subject: [PATCH 04/35] Add documentation to the Compose page (HD-24) --- src/pages/Compose.tsx | 189 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/src/pages/Compose.tsx b/src/pages/Compose.tsx index 0cee323..47ab87b 100644 --- a/src/pages/Compose.tsx +++ b/src/pages/Compose.tsx @@ -45,34 +45,104 @@ import { getUserDefaultBool } from "../utilities/settings"; +/** + * The state for the Composer page. + */ interface IComposerState { + /** + * The current user as an Account. + */ account: UAccount; + + /** + * The visibility of the post. + */ visibility: Visibility; + + /** + * Whether there should be a content warning. + */ sensitive: boolean; + + /** + * The content warning message. + */ sensitiveText?: string; + + /** + * Whether the visibility drop-down should be visible. + */ visibilityMenu: boolean; + + /** + * The text contents of the post. + */ text: string; + + /** + * The remaining amount of characters. + */ remainingChars: number; + + /** + * An optional reply ID. + */ reply?: string; + + /** + * The account to reply to, if it exists. + */ acct?: string; + + /** + * An optional list of media attachments. + */ attachments?: [Attachment]; + + /** + * An optional poll for the post. + */ poll?: PollWizard; + + /** + * The expiration date of a poll, if it exists. + */ pollExpiresDate?: any; + + /** + * Whether the emoji picker should be visible. + */ showEmojis: boolean; + + /** + * Whether or not the account's instance is federated. + */ federated: boolean; } +/** + * The Compose page contains all of the information to create a UI for post creation. + */ class Composer extends Component { + /** + * The Mastodon client to work with. + */ client: Mastodon; + /** + * Construct the Compose page by generating the Mastodon client and setting default values. + * @param props The properties passed into the Compose component, usually the page queries. + */ constructor(props: any) { super(props); + // Generate the Mastodon client this.client = new Mastodon( localStorage.getItem("access_token") as string, localStorage.getItem("baseurl") + "/api/v1" ); + // Set the initial state this.state = { account: JSON.parse(localStorage.getItem("account") as string), visibility: getUserDefaultVisibility(), @@ -87,13 +157,21 @@ class Composer extends Component { }; } + /** + * Run any additional state checks and setup once the page has mounted. This includes + * parsing the query parameters and loading the configuration, as well as defining the + * clipboard listener. + */ componentDidMount() { + // Parse the parameters and get the account information if available. let state = this.getComposerParams(this.props); let text = state.acct ? `@${state.acct}: ` : ""; this.client.get("/accounts/verify_credentials").then((resp: any) => { let account: UAccount = resp.data; this.setState({ account }); }); + + // Get the configuration and load the config values. getConfig().then((config: any) => { this.setState({ federated: config.federation.allowPublicPosts, @@ -107,6 +185,8 @@ class Composer extends Component { }); }); + // Attach the paste listener to listen for the clipboard and upload media + // if possible. window.addEventListener("paste", (evt: Event) => { let thePasteEvent = evt as ClipboardEvent; let fileList: File[] = []; @@ -130,6 +210,12 @@ class Composer extends Component { }); } + /** + * Reload the properties and set the state to those new properties. This usually + * occurs when the page is either reloaded or changes but React doesn't see the + * properties change. + * @param props The properties passed into the Compose component, usually the page queries. + */ componentWillReceiveProps(props: any) { let state = this.getComposerParams(props); let text = state.acct ? `@${state.acct}: ` : ""; @@ -144,6 +230,11 @@ class Composer extends Component { }); } + /** + * Check the location string and attempt to parse it into a parsed query. + * @param location The location string from React Router. + * @returns The ParsedQuery object containing all of the parameters. + */ checkComposerParams(location?: string): ParsedQuery { let params = ""; if (location !== undefined && typeof location === "string") { @@ -154,6 +245,11 @@ class Composer extends Component { return parseParams(params); } + /** + * Check the property's location string, parse it, and return it. + * @param props The properties passed into the Compose component, usually the page queries. + * @returns An object containing the reply ID, reply account, and visibility. + */ getComposerParams(props: any) { let params = this.checkComposerParams(props.location); let reply: string = ""; @@ -176,6 +272,10 @@ class Composer extends Component { }; } + /** + * Update the text in the state and calculate the remaining character length. + * @param text The text to update the state to + */ updateTextFromField(text: string) { this.setState({ text, @@ -185,14 +285,25 @@ class Composer extends Component { }); } + /** + * Update the content warning text in the state + * @param sensitiveText The text to update the state to + */ updateWarningFromField(sensitiveText: string) { this.setState({ sensitiveText }); } + /** + * Update the visibility in the state + * @param visibility The visibility to update the state to + */ changeVisibility(visibility: Visibility) { this.setState({ visibility }); } + /** + * Open a file dialog to let the user choose files to upload to the server and then upload them. + */ promptMediaDialog() { filedialog({ multiple: false, @@ -207,15 +318,27 @@ class Composer extends Component { }); } + /** + * Upload a list of files to Mastodon as attachments. Reads the first item in the list. + * This also updates the attachments state after a successful upload. + * @param media The list of files (`FileList` or `File[]`) to send to Mastodon. + */ uploadMedia(media: FileList | File[]) { + // Create a new FormData for Mastodon let mediaForm = new FormData(); mediaForm.append("file", media[0]); + + // Let the user know we're uploading the file this.props.enqueueSnackbar("Uploading media...", { persist: true, key: "media-upload" }); + + // Try to upload the media to the server. this.client .post("/media", mediaForm) + + // If we succeed, get the attachments and update the state. .then((resp: any) => { let attachment: Attachment = resp.data; let attachments = this.state.attachments; @@ -228,6 +351,8 @@ class Composer extends Component { this.props.closeSnackbar("media-upload"); this.props.enqueueSnackbar("Media uploaded."); }) + + // If we fail, display an error. .catch((err: Error) => { this.props.closeSnackbar("media-upload"); this.props.enqueueSnackbar( @@ -237,6 +362,10 @@ class Composer extends Component { }); } + /** + * Iterate through the attachments and grab the attachments' IDs. + * @returns A list of IDs as `string[]` + */ getOnlyMediaIds() { let ids: string[] = []; if (this.state.attachments) { @@ -247,6 +376,10 @@ class Composer extends Component { return ids; } + /** + * Update the list of attachments by inserting an attachment. + * @param attachment The attachment to insert into the attachments list. + */ fetchAttachmentAfterUpdate(attachment: Attachment) { let attachments = this.state.attachments; if (attachments) { @@ -259,6 +392,10 @@ class Composer extends Component { } } + /** + * Remove an attachment from the list of attachments and update the state. + * @param attachment The attachment to remove from the list + */ deleteMediaAttachment(attachment: Attachment) { let attachments = this.state.attachments; if (attachments) { @@ -272,6 +409,10 @@ class Composer extends Component { } } + /** + * Insert an emoji at the end of text string and update the state + * @param e The emoji to insert into the text + */ insertEmoji(e: any) { if (e.custom) { let text = this.state.text + e.colons; @@ -288,6 +429,9 @@ class Composer extends Component { } } + /** + * Create an empty poll. + */ createPoll() { if (this.state.poll === undefined) { let expiration = new Date(); @@ -307,6 +451,9 @@ class Composer extends Component { } } + /** + * Insert a new poll item into the poll. + */ addPollItem() { if ( this.state.poll !== undefined && @@ -329,6 +476,11 @@ class Composer extends Component { } } + /** + * Edit an existing poll item with new text + * @param position The position of the poll item in the list + * @param newTitle The new text to update + */ editPollItem(position: number, newTitle: any) { if (this.state.poll !== undefined) { let poll = this.state.poll; @@ -346,6 +498,10 @@ class Composer extends Component { } } + /** + * Removes a poll item from the poll + * @param item The item to remove + */ removePollItem(item: string) { if ( this.state.poll !== undefined && @@ -372,6 +528,10 @@ class Composer extends Component { } } + /** + * Set the expiration date of the poll. + * @param date The new expiration date + */ setPollExpires(date: string) { let currentDate = new Date(); let newDate = new Date(date); @@ -391,25 +551,38 @@ class Composer extends Component { } } + /** + * Remove the poll from the post. + */ removePoll() { this.setState({ poll: undefined }); } + /** + * Check if the user presses the Ctrl/Cmd+Enter key and post to the server if possible. + * @param event The keyboard event + */ postViaKeyboard(event: any) { if ((event.metaKey || event.ctrlKey) && event.keyCode === 13) { this.post(); } } + /** + * Send the post to Mastodon and return to the previous page, if possible. + */ post() { + // First, finalize the poll. let pollOptions: string[] = []; if (this.state.poll) { this.state.poll.options.forEach((option: PollWizardOption) => { pollOptions.push(option.title); }); } + + // Send a post request to Mastodon. this.client .post("/statuses", { status: this.state.text, @@ -426,28 +599,44 @@ class Composer extends Component { } : null }) + + // If we succeed, send a success message and go back. .then(() => { this.props.enqueueSnackbar("Posted!"); window.history.back(); }) + + // Otherwise, show an error message and don't do anything. .catch((err: Error) => { this.props.enqueueSnackbar("Couldn't post: " + err.name); console.error(err.message); }); } + /** + * Toggle the content warning section. + */ toggleSensitive() { this.setState({ sensitive: !this.state.sensitive }); } + /** + * Toggle the visibility drop down menu. + */ toggleVisibilityMenu() { this.setState({ visibilityMenu: !this.state.visibilityMenu }); } + /** + * Toggle the emoji picker. + */ toggleEmojis() { this.setState({ showEmojis: !this.state.showEmojis }); } + /** + * Render all of the components on the page given a set of classes. + */ render() { const { classes } = this.props; From 93f135e93b6e8154961505187c7e73c314a427f5 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Wed, 11 Dec 2019 12:27:42 -0500 Subject: [PATCH 05/35] Create basic back button (HD-28) --- src/components/AppLayout/AppLayout.styles.tsx | 4 ++++ src/components/AppLayout/AppLayout.tsx | 13 +++++++++++++ src/utilities/appbar.tsx | 17 +++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/components/AppLayout/AppLayout.styles.tsx b/src/components/AppLayout/AppLayout.styles.tsx index 8c9824d..7f8116c 100644 --- a/src/components/AppLayout/AppLayout.styles.tsx +++ b/src/components/AppLayout/AppLayout.styles.tsx @@ -57,6 +57,10 @@ export const styles = (theme: Theme) => display: "none" } }, + appBarBackButton: { + marginLeft: -12, + marginRight: 20 + }, appBarTitle: { display: "none", [theme.breakpoints.up("md")]: { diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index a3f89d5..ef4ef34 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -44,6 +44,7 @@ import SupervisedUserCircleIcon from "@material-ui/icons/SupervisedUserCircle"; import ExitToAppIcon from "@material-ui/icons/ExitToApp"; import TrendingUpIcon from "@material-ui/icons/TrendingUp"; import BuildIcon from "@material-ui/icons/Build"; +import ArrowBackIcon from "@material-ui/icons/ArrowBack"; import { styles } from "./AppLayout.styles"; import { MultiAccount, UAccount } from "../../types/Account"; @@ -67,6 +68,7 @@ import { getAccountRegistry, removeAccountFromRegistry } from "../../utilities/accounts"; +import { isChildView } from "../../utilities/appbar"; interface IAppLayoutState { acctMenuOpen: boolean; @@ -484,6 +486,17 @@ export class AppLayout extends Component { {this.titlebar()} + {isChildView(window.location.hash) ? ( + window.history.back()} + > + + + ) : null} + { + if (hash.includes(childViewProtocol)) { + console.log(childViewProtocol); + foundProtocol = true; + } + }); + + return foundProtocol; +} From e761dc4f97abb55a62e4297520e0c80fed1b9580 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Wed, 11 Dec 2019 12:55:38 -0500 Subject: [PATCH 06/35] Rely on just window history --- src/components/AppLayout/AppLayout.tsx | 7 +++++-- src/utilities/appbar.tsx | 17 ----------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index ef4ef34..3660ae8 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -68,7 +68,6 @@ import { getAccountRegistry, removeAccountFromRegistry } from "../../utilities/accounts"; -import { isChildView } from "../../utilities/appbar"; interface IAppLayoutState { acctMenuOpen: boolean; @@ -223,6 +222,10 @@ export class AppLayout extends Component { }); } + canGoBack(): boolean { + return window.history.length > 1; + } + toggleAcctMenu() { this.setState({ acctMenuOpen: !this.state.acctMenuOpen }); } @@ -486,7 +489,7 @@ export class AppLayout extends Component { {this.titlebar()} - {isChildView(window.location.hash) ? ( + {isDesktopApp() && this.canGoBack() ? ( { - if (hash.includes(childViewProtocol)) { - console.log(childViewProtocol); - foundProtocol = true; - } - }); - - return foundProtocol; -} From 2ffa744e528a5bd8e8d69e3bf48949172e61fcb5 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Wed, 11 Dec 2019 13:21:28 -0500 Subject: [PATCH 07/35] Re-instate original idea --- src/components/AppLayout/AppLayout.tsx | 3 ++- src/utilities/appbar.tsx | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index 3660ae8..a142bb3 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -68,6 +68,7 @@ import { getAccountRegistry, removeAccountFromRegistry } from "../../utilities/accounts"; +import { isChildView } from "../../utilities/appbar"; interface IAppLayoutState { acctMenuOpen: boolean; @@ -489,7 +490,7 @@ export class AppLayout extends Component { {this.titlebar()} - {isDesktopApp() && this.canGoBack() ? ( + {isDesktopApp() && isChildView() ? ( { + if (path.startsWith(childViewProtocol)) { + protocolMatched = true; + } + }); + return protocolMatched; +} From 774e2eb817fe9246647c946312504eca63f41847 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Wed, 11 Dec 2019 13:37:42 -0500 Subject: [PATCH 08/35] Document AppLayout and pass in window location hash --- src/components/AppLayout/AppLayout.tsx | 110 +++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 5 deletions(-) diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index a142bb3..3b82fa7 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -70,29 +70,79 @@ import { } from "../../utilities/accounts"; import { isChildView } from "../../utilities/appbar"; +/** + * The pre-define state interface for the app layout. + */ interface IAppLayoutState { + /** + * Whether the account menu is open or not. + */ acctMenuOpen: boolean; + + /** + * Whether the drawer is open (mobile-only). + */ drawerOpenOnMobile: boolean; + + /** + * The current user signed in. + */ currentUser?: UAccount; + + /** + * The number of notifications received. + */ notificationCount: number; + + /** + * Whether the log out dialog is open. + */ logOutOpen: boolean; + + /** + * Whether federation has been enabled in the config. + */ enableFederation?: boolean; + + /** + * The brand name of the app, if not "Hyperspace". + */ brandName?: string; + + /** + * Whether the app is in development mode. + */ developerMode?: boolean; } +/** + * The base app layout class. Responsible for the search bar, navigation menus, etc. + */ export class AppLayout extends Component { + /** + * The Mastodon client to operate with. + */ client: Mastodon; + + /** + * A stream listener to listen for new streaming events from Mastodon. + */ streamListener: any; + /** + * Construct the app layout. + * @param props The properties to pass in. + */ constructor(props: any) { super(props); + // Create the Mastodon client this.client = new Mastodon( localStorage.getItem("access_token") as string, (localStorage.getItem("baseurl") as string) + "/api/v1" ); + // Initialize the state this.state = { drawerOpenOnMobile: false, acctMenuOpen: false, @@ -100,14 +150,20 @@ export class AppLayout extends Component { logOutOpen: false }; + // Bind functions as properties to this class for reference this.toggleDrawerOnMobile = this.toggleDrawerOnMobile.bind(this); this.toggleAcctMenu = this.toggleAcctMenu.bind(this); this.clearBadge = this.clearBadge.bind(this); } + /** + * Run post-mount tasks such as getting account data and refreshing the config file. + */ componentDidMount() { + // Get the account data. this.getAccountData(); + // Read the config file and then update the state. getConfig().then((result: any) => { if (result !== undefined) { let config: Config = result; @@ -121,18 +177,25 @@ export class AppLayout extends Component { } }); + // Listen for notifications. this.streamNotifications(); } + /** + * Get updated credentials from Mastodon or pull information from local storage. + */ getAccountData() { + // Try to get updated credentials from Mastodon. this.client .get("/accounts/verify_credentials") .then((resp: any) => { + // Update the account if possible. let data: UAccount = resp.data; this.setState({ currentUser: data }); sessionStorage.setItem("id", data.id); }) .catch((err: Error) => { + // Otherwise, pull from local storage. this.props.enqueueSnackbar( "Couldn't find profile info: " + err.name ); @@ -142,9 +205,14 @@ export class AppLayout extends Component { }); } + /** + * Set up a stream listener and listen for notifications. + */ streamNotifications() { + // Set up the stream listener. this.streamListener = this.client.stream("/streaming/user"); + // Set the count if the user asked to display the total count. if (getUserDefaultBool("displayAllOnNotificationBadge")) { this.client.get("/notifications").then((resp: any) => { let notifArray = resp.data; @@ -152,14 +220,17 @@ export class AppLayout extends Component { }); } + // Listen for notifications. this.streamListener.on("notification", (notif: Notification) => { const notificationCount = this.state.notificationCount + 1; this.setState({ notificationCount }); + // Update the badge on the desktop. if (isDesktopApp()) { getElectronApp().setBadgeCount(notificationCount); } + // Set up a push notification if the window isn't in focus. if (!document.hasFocus()) { let primaryMessage = ""; let secondaryMessage = ""; @@ -218,29 +289,39 @@ export class AppLayout extends Component { break; } + // Respectfully send the notification request. sendNotificationRequest(primaryMessage, secondaryMessage); } }); } - canGoBack(): boolean { - return window.history.length > 1; - } - + /** + * Toggle the account menu. + */ toggleAcctMenu() { this.setState({ acctMenuOpen: !this.state.acctMenuOpen }); } + /** + * Toggle the app drawer, if on mobile. + */ toggleDrawerOnMobile() { this.setState({ drawerOpenOnMobile: !this.state.drawerOpenOnMobile }); } + /** + * Toggle the logout dialog. + */ toggleLogOutDialog() { this.setState({ logOutOpen: !this.state.logOutOpen }); } + /** + * Perform a search and redirect to the search page. + * @param what The query input from the search box + */ searchForQuery(what: string) { what = what.replace(/^#/g, "tag:"); console.log(what); @@ -249,9 +330,13 @@ export class AppLayout extends Component { : "/#/search?query=" + what; } + /** + * Clear login information, remove the account from the registry, and reload the web page. + */ logOutAndRestart() { let loginData = localStorage.getItem("login"); if (loginData) { + // Remove account from the registry. let registry = getAccountRegistry(); registry.forEach((registryItem: MultiAccount, index: number) => { @@ -263,15 +348,20 @@ export class AppLayout extends Component { } }); + // Clear some of the local storage fields. let items = ["login", "account", "baseurl", "access_token"]; items.forEach(entry => { localStorage.removeItem(entry); }); + // Finally, reload. window.location.reload(); } } + /** + * Clear the notifications badge. + */ clearBadge() { if (!getUserDefaultBool("displayAllOnNotificationBadge")) { this.setState({ notificationCount: 0 }); @@ -282,6 +372,9 @@ export class AppLayout extends Component { } } + /** + * Render the title bar. + */ titlebar() { const { classes } = this.props; if (isDarwinApp()) { @@ -313,6 +406,9 @@ export class AppLayout extends Component { } } + /** + * Render the app drawer. On the desktop, this appears as a sidebar in larger layouts. + */ appDrawer() { const { classes } = this.props; return ( @@ -482,6 +578,9 @@ export class AppLayout extends Component { ); } + /** + * Render the entire layout. + */ render() { const { classes } = this.props; return ( @@ -490,7 +589,8 @@ export class AppLayout extends Component { {this.titlebar()} - {isDesktopApp() && isChildView() ? ( + {isDesktopApp() && + isChildView(window.location.hash) ? ( Date: Wed, 11 Dec 2019 14:32:07 -0500 Subject: [PATCH 09/35] Add drafts utilities, draft segment, and draft type --- src/pages/Compose.styles.tsx | 20 +++++++++++++++++ src/pages/Compose.tsx | 41 ++++++++++++++++++++++++++++++++++ src/types/Draft.tsx | 13 +++++++++++ src/utilities/compose.tsx | 43 ++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 src/types/Draft.tsx create mode 100644 src/utilities/compose.tsx diff --git a/src/pages/Compose.styles.tsx b/src/pages/Compose.styles.tsx index 6d354ea..40ea774 100644 --- a/src/pages/Compose.styles.tsx +++ b/src/pages/Compose.styles.tsx @@ -45,5 +45,25 @@ export const styles = (theme: Theme) => }, pollWizardFlexGrow: { flexGrow: 1 + }, + draftDisplayArea: { + display: "flex", + paddingLeft: 8, + paddingRight: 8, + paddingTop: 4, + paddingBottom: 4, + borderColor: theme.palette.action.disabledBackground, + borderWidth: 0.25, + borderStyle: "solid", + borderRadius: 2, + verticalAlign: "middle", + marginLeft: 16, + marginRight: 16 + }, + draftText: { + padding: theme.spacing.unit / 2 + }, + draftFlexGrow: { + flexGrow: 1 } }); diff --git a/src/pages/Compose.tsx b/src/pages/Compose.tsx index fa8915f..1529664 100644 --- a/src/pages/Compose.tsx +++ b/src/pages/Compose.tsx @@ -44,6 +44,7 @@ import { getConfig, getUserDefaultBool } from "../utilities/settings"; +import { draftExists, writeDraft, loadDraft } from "../utilities/compose"; interface IComposerState { account: UAccount; @@ -141,6 +142,31 @@ class Composer extends Component { }); } + /** + * Check if there is unsaved text and store it as a draft. + */ + componentWillUnmount() { + if (this.state.text !== "") { + writeDraft( + this.state.text, + this.state.reply ? Number(this.state.reply) : -999 + ); + this.props.enqueueSnackbar("Draft saved."); + } + } + + /** + * Restore the draft from session storage and pre-load it into the state. + */ + restoreDraft() { + const draft = loadDraft(); + const text = draft.contents; + const reply = + draft.replyId !== -999 ? draft.replyId.toString() : undefined; + this.setState({ text, reply }); + this.props.enqueueSnackbar("Restored draft."); + } + checkComposerParams(location?: string): ParsedQuery { let params = ""; if (location !== undefined && typeof location === "string") { @@ -738,6 +764,21 @@ class Composer extends Component { ) : null} + {draftExists() ? ( + + + You have an unsaved post. + +
+ + + ) : null}
- ) : null} + ) : ( +
+
+
+
+ +
+ + {'Loading...'} + + + @{'...'} + +
+
+ +
+
+ )}
Appearance From 8a39f36039816550cd140eb1cee4a5a1406819f5 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Wed, 18 Dec 2019 11:08:51 -0500 Subject: [PATCH 14/35] Manually apply #140 (HD-29 #done) --- src/components/AppLayout/AppLayout.tsx | 2 +- src/pages/Settings.tsx | 60 +++++++++++++------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/components/AppLayout/AppLayout.tsx b/src/components/AppLayout/AppLayout.tsx index a3f89d5..8c75d51 100644 --- a/src/components/AppLayout/AppLayout.tsx +++ b/src/components/AppLayout/AppLayout.tsx @@ -675,7 +675,7 @@ export class AppLayout extends Component { variant="temporary" anchor={"left"} open={this.state.drawerOpenOnMobile} - onClose={this.toggleDrawerOnMobile} + onClick={this.toggleDrawerOnMobile} classes={{ paper: classes.drawerPaper }} > {this.appDrawer()} diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index d5d8878..0607411 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -371,7 +371,7 @@ class SettingsPage extends Component { this.state.federated ? "" : "(disabled by provider)" - }`} + }`} disabled={!this.state.federated} /> {
) : ( -
-
-
-
- -
+
+
+
+ +
+ - - {'Loading...'} - - - @{'...'} - -
-
- + {"Loading..."} + + + @{"..."} +
+
+
- )} +
+ )}
Appearance @@ -713,8 +711,8 @@ class SettingsPage extends Component { ) ? "Check your browser's notification permissions." : browserSupportsNotificationRequests() - ? "Sends a push notification when not focused." - : "Notifications aren't supported." + ? "Sends a push notification when not focused." + : "Notifications aren't supported." } /> From add9590e0b3a253eedb7fceda594ccfa371fbdcb Mon Sep 17 00:00:00 2001 From: Travis Kohlbeck Date: Wed, 18 Dec 2019 13:03:16 -0500 Subject: [PATCH 15/35] updates appearance of post/retoot authors --- src/components/Post/Post.styles.tsx | 9 ++++++ src/components/Post/Post.tsx | 43 +++++++++++++++++------------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/components/Post/Post.styles.tsx b/src/components/Post/Post.styles.tsx index 362d55a..1fa5377 100644 --- a/src/components/Post/Post.styles.tsx +++ b/src/components/Post/Post.styles.tsx @@ -81,6 +81,15 @@ export const styles = (theme: Theme) => paddingTop: theme.spacing.unit, paddingBottom: theme.spacing.unit }, + postAuthorAccount: { + color: theme.palette.grey[500], + marginLeft: theme.spacing.unit * 0.5, + }, + postReblogIcon: { + marginBottom: theme.spacing.unit * -0.5, + marginLeft: theme.spacing.unit * 0.5, + marginRight: theme.spacing.unit * 0.5, + }, postAuthorEmoji: { height: theme.typography.fontSize, verticalAlign: "middle" diff --git a/src/components/Post/Post.tsx b/src/components/Post/Post.tsx index a6d2621..cc8fc5b 100644 --- a/src/components/Post/Post.tsx +++ b/src/components/Post/Post.tsx @@ -398,21 +398,34 @@ export class Post extends React.Component { const { classes } = this.props; if (post.reblog) { let author = post.reblog.account; - let origString = `${author.display_name || - author.username} (@${author.acct}) 🔄 ${post.account - .display_name || post.account.username}`; let emojis = author.emojis; emojis.concat(post.account.emojis); - return emojifyString(origString, emojis, classes.postAuthorEmoji); + return ( + <> + + {emojifyString(author.display_name || author.username, author.emojis, classes.postAuthorEmoji)} + + + @{emojifyString(author.acct, author.emojis, classes.postAuthorEmoji)} + + + + {emojifyString(post.account.display_name || post.account.username, emojis, classes.postAuthorEmoji)} + + + ) } else { let author = post.account; - let origString = `${author.display_name || - author.username} (@${author.acct})`; - return emojifyString( - origString, - author.emojis, - classes.postAuthorEmoji - ); + return ( + <> + + {emojifyString(author.display_name || author.username, author.emojis, classes.postAuthorEmoji)} + + + @{emojifyString(author.acct, author.emojis, classes.postAuthorEmoji)} + + + ) } } @@ -656,13 +669,7 @@ export class Post extends React.Component { } - title={ - - } + title={{this.getReblogAuthors(post)}} subheader={moment(post.created_at).format( "MMMM Do YYYY [at] h:mm A" )} From fc8afb9000b34a8d6a987bd5c9076dfa00e3bf6c Mon Sep 17 00:00:00 2001 From: Travis Kohlbeck Date: Wed, 18 Dec 2019 13:03:16 -0500 Subject: [PATCH 16/35] updates appearance of post/retoot authors --- src/components/Post/Post.styles.tsx | 9 ++++++ src/components/Post/Post.tsx | 43 +++++++++++++++++------------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/components/Post/Post.styles.tsx b/src/components/Post/Post.styles.tsx index 362d55a..1fa5377 100644 --- a/src/components/Post/Post.styles.tsx +++ b/src/components/Post/Post.styles.tsx @@ -81,6 +81,15 @@ export const styles = (theme: Theme) => paddingTop: theme.spacing.unit, paddingBottom: theme.spacing.unit }, + postAuthorAccount: { + color: theme.palette.grey[500], + marginLeft: theme.spacing.unit * 0.5, + }, + postReblogIcon: { + marginBottom: theme.spacing.unit * -0.5, + marginLeft: theme.spacing.unit * 0.5, + marginRight: theme.spacing.unit * 0.5, + }, postAuthorEmoji: { height: theme.typography.fontSize, verticalAlign: "middle" diff --git a/src/components/Post/Post.tsx b/src/components/Post/Post.tsx index a6d2621..cc8fc5b 100644 --- a/src/components/Post/Post.tsx +++ b/src/components/Post/Post.tsx @@ -398,21 +398,34 @@ export class Post extends React.Component { const { classes } = this.props; if (post.reblog) { let author = post.reblog.account; - let origString = `${author.display_name || - author.username} (@${author.acct}) 🔄 ${post.account - .display_name || post.account.username}`; let emojis = author.emojis; emojis.concat(post.account.emojis); - return emojifyString(origString, emojis, classes.postAuthorEmoji); + return ( + <> + + {emojifyString(author.display_name || author.username, author.emojis, classes.postAuthorEmoji)} + + + @{emojifyString(author.acct, author.emojis, classes.postAuthorEmoji)} + + + + {emojifyString(post.account.display_name || post.account.username, emojis, classes.postAuthorEmoji)} + + + ) } else { let author = post.account; - let origString = `${author.display_name || - author.username} (@${author.acct})`; - return emojifyString( - origString, - author.emojis, - classes.postAuthorEmoji - ); + return ( + <> + + {emojifyString(author.display_name || author.username, author.emojis, classes.postAuthorEmoji)} + + + @{emojifyString(author.acct, author.emojis, classes.postAuthorEmoji)} + + + ) } } @@ -656,13 +669,7 @@ export class Post extends React.Component { } - title={ - - } + title={{this.getReblogAuthors(post)}} subheader={moment(post.created_at).format( "MMMM Do YYYY [at] h:mm A" )} From e178a01fff417afd9149d8f5d05afa9e968c9f11 Mon Sep 17 00:00:00 2001 From: Travis Kohlbeck Date: Sat, 21 Dec 2019 17:14:50 -0500 Subject: [PATCH 17/35] implements masonry layout with breakpoints --- src/pages/Home.tsx | 24 +++++++++++++++++++----- src/pages/PageLayout.styles.tsx | 23 +++++++++++++++-------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 878ad6b..b4dacd9 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -14,6 +14,7 @@ import Post from "../components/Post"; import { Status } from "../types/Status"; import Mastodon, { StreamListener } from "megalodon"; import { withSnackbar } from "notistack"; +import Masonry from 'react-masonry-css' import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; interface IHomePageState { @@ -185,15 +186,28 @@ class HomePage extends Component { ) : null} {this.state.posts ? (
+ {this.state.posts.map((post: Status) => { return ( - +
+ +
); })} +

{this.state.viewDidLoad && !this.state.viewDidError ? (
marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, - paddingLeft: theme.spacing.unit * 16, - paddingRight: theme.spacing.unit * 16 + paddingLeft: theme.spacing.unit * 3, + paddingRight: theme.spacing.unit * 3 }, [theme.breakpoints.up("lg")]: { marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, - paddingLeft: theme.spacing.unit * 32, - paddingRight: theme.spacing.unit * 32 + paddingLeft: theme.spacing.unit * 3, + paddingRight: theme.spacing.unit * 3 }, [theme.breakpoints.up("xl")]: { marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, - paddingLeft: theme.spacing.unit * 40, - paddingRight: theme.spacing.unit * 40 + paddingLeft: theme.spacing.unit * 3, + paddingRight: theme.spacing.unit * 3 }, backgroundColor: theme.palette.background.default, minHeight: isDarwinApp() ? "100vh" : "auto" @@ -323,5 +323,12 @@ export const styles = (theme: Theme) => display: "block" }, backgroundColor: theme.palette.primary.main - } + }, + masonryGrid: { + display: 'flex', + width: 'auto', + }, + 'my-masonry-grid_column': { // non-standard name fixes react-masonry-css bug :shrug: + padding: 5, + }, }); From af4f6e1d539e8038a1065a8eebd1bacf693d00e3 Mon Sep 17 00:00:00 2001 From: Travis Kohlbeck Date: Sat, 21 Dec 2019 17:57:26 -0500 Subject: [PATCH 18/35] adds settings option for masonry layout --- src/pages/Home.tsx | 69 +++++++++++++++++++++++++------------- src/pages/Settings.tsx | 30 ++++++++++++++++- src/utilities/settings.tsx | 6 ++-- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index b4dacd9..90f0354 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -16,6 +16,10 @@ import Mastodon, { StreamListener } from "megalodon"; import { withSnackbar } from "notistack"; import Masonry from 'react-masonry-css' import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; +import { + getConfig, + getUserDefaultBool +} from "../utilities/settings"; interface IHomePageState { posts?: [Status]; @@ -24,6 +28,7 @@ interface IHomePageState { viewDidLoad?: boolean; viewDidError?: boolean; viewDidErrorCode?: any; + isMasonryLayout?: boolean; } class HomePage extends Component { @@ -35,7 +40,8 @@ class HomePage extends Component { this.state = { viewIsLoading: true, - backlogPosts: null + backlogPosts: null, + isMasonryLayout: getUserDefaultBool('isMasonryLayout') }; this.client = new Mastodon( @@ -186,28 +192,45 @@ class HomePage extends Component { ) : null} {this.state.posts ? (
- - {this.state.posts.map((post: Status) => { - return ( -
- -
- ); - })} -
+ {this.state.isMasonryLayout ? ( + + {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} +
) + : ( +
+ {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} +
+ ) + }
{this.state.viewDidLoad && !this.state.viewDidError ? (
{ @@ -117,7 +119,8 @@ class SettingsPage extends Component { defaultVisibility: getUserDefaultVisibility() || "public", brandName: "Hyperspace", federated: true, - imposeCharacterLimit: getUserDefaultBool("imposeCharacterLimit") + imposeCharacterLimit: getUserDefaultBool("imposeCharacterLimit"), + masonryLayout: getUserDefaultBool("isMasonryLayout"), }; this.toggleDarkMode = this.toggleDarkMode.bind(this); @@ -126,6 +129,7 @@ class SettingsPage extends Component { this.toggleBadgeCount = this.toggleBadgeCount.bind(this); this.toggleThemeDialog = this.toggleThemeDialog.bind(this); this.toggleVisibilityDialog = this.toggleVisibilityDialog.bind(this); + this.toggleMasonryLayout = this.toggleMasonryLayout.bind(this) this.changeThemeName = this.changeThemeName.bind(this); this.changeTheme = this.changeTheme.bind(this); this.setVisibility = this.setVisibility.bind(this); @@ -241,6 +245,14 @@ class SettingsPage extends Component { this.setState({ resetSettingsDialog: !this.state.resetSettingsDialog }); } + toggleMasonryLayout() { + this.setState({ masonryLayout: !this.state.masonryLayout }) + setUserDefaultBool( + "isMasonryLayout", + !this.state.masonryLayout + ) + } + changeTheme() { setUserDefaultTheme(this.state.selectThemeName); window.location.reload(); @@ -650,6 +662,22 @@ class SettingsPage extends Component { + + + + + + + + + +
diff --git a/src/utilities/settings.tsx b/src/utilities/settings.tsx index 43b36ee..93be298 100644 --- a/src/utilities/settings.tsx +++ b/src/utilities/settings.tsx @@ -101,7 +101,8 @@ export function createUserDefaults() { clearNotificationsOnRead: false, displayAllOnNotificationBadge: false, defaultVisibility: "public", - imposeCharacterLimit: true + imposeCharacterLimit: true, + isMasonryLayout: false, }; let settings = [ @@ -110,7 +111,8 @@ export function createUserDefaults() { "clearNotificationsOnRead", "displayAllOnNotificationBadge", "defaultVisibility", - "imposeCharacterLimit" + "imposeCharacterLimit", + "isMasonryLayout", ]; migrateExistingSettings(); From 95e5849be878a9a817432902f7558744f9e23d8f Mon Sep 17 00:00:00 2001 From: Travis Kohlbeck Date: Sat, 21 Dec 2019 18:10:24 -0500 Subject: [PATCH 19/35] adds masonry layout to local and public feeds --- package.json | 3 ++- src/pages/Home.tsx | 12 ++++------ src/pages/Local.tsx | 53 +++++++++++++++++++++++++++++++++++--------- src/pages/Public.tsx | 53 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 92 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 1354618..ae2a9a4 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "dependencies": { "electron-notarize": "^0.1.1", "electron-updater": "^4.1.2", - "electron-window-state": "^5.0.3" + "electron-window-state": "^5.0.3", + "react-masonry-css": "^1.0.14" }, "main": "public/electron.js", "scripts": { diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 90f0354..7a3aced 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -15,11 +15,8 @@ import { Status } from "../types/Status"; import Mastodon, { StreamListener } from "megalodon"; import { withSnackbar } from "notistack"; import Masonry from 'react-masonry-css' +import { getUserDefaultBool } from "../utilities/settings"; import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; -import { - getConfig, - getUserDefaultBool -} from "../utilities/settings"; interface IHomePageState { posts?: [Status]; @@ -214,8 +211,8 @@ class HomePage extends Component {
); })} - ) - : ( + + ) : (
{this.state.posts.map((post: Status) => { return ( @@ -229,8 +226,7 @@ class HomePage extends Component { ); })}
- ) - } + )}
{this.state.viewDidLoad && !this.state.viewDidError ? (
{ @@ -34,7 +37,8 @@ class LocalPage extends Component { this.state = { viewIsLoading: true, - backlogPosts: null + backlogPosts: null, + isMasonryLayout: getUserDefaultBool('isMasonryLayout'), }; this.client = new Mastodon( @@ -186,15 +190,44 @@ class LocalPage extends Component { ) : null} {this.state.posts ? (
- {this.state.posts.map((post: Status) => { - return ( - - ); - })} + {this.state.isMasonryLayout ? ( + + {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} +
+ ) : ( +
+ {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} +
+ )}
{this.state.viewDidLoad && !this.state.viewDidError ? (
{ @@ -34,7 +37,8 @@ class PublicPage extends Component { this.state = { viewIsLoading: true, - backlogPosts: null + backlogPosts: null, + isMasonryLayout: getUserDefaultBool('isMasonryLayout') }; this.client = new Mastodon( @@ -185,15 +189,44 @@ class PublicPage extends Component { ) : null} {this.state.posts ? (
- {this.state.posts.map((post: Status) => { - return ( - - ); - })} + {this.state.isMasonryLayout ? ( + + {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} +
+ ) : ( +
+ {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} +
+ )}
{this.state.viewDidLoad && !this.state.viewDidError ? (
Date: Sat, 21 Dec 2019 21:28:23 -0500 Subject: [PATCH 20/35] changes settings title for masonry layout --- src/pages/Settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index ab8c2f7..62e4d73 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -668,7 +668,7 @@ class SettingsPage extends Component { From 313ff79116065eb7e2a20c4e7d19ec6f2d360571 Mon Sep 17 00:00:00 2001 From: Travis Kohlbeck Date: Sun, 22 Dec 2019 16:05:24 -0500 Subject: [PATCH 21/35] adds back original padding when masonry disabled --- src/pages/Home.tsx | 20 +++++++++++--------- src/pages/PageLayout.styles.tsx | 16 ++++++++++------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 7a3aced..062e39c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -158,9 +158,13 @@ class HomePage extends Component { render() { const { classes } = this.props; - + const containerClasses = `${classes.pageLayoutMaxConstraints}${ + this.state.isMasonryLayout + ? ' ' + classes.pageLayoutMasonry + : '' + }`; return ( -
+
{this.state.backlogPosts ? (
@@ -216,13 +220,11 @@ class HomePage extends Component {
{this.state.posts.map((post: Status) => { return ( -
- -
+ ); })}
diff --git a/src/pages/PageLayout.styles.tsx b/src/pages/PageLayout.styles.tsx index e573f7c..8d0c670 100644 --- a/src/pages/PageLayout.styles.tsx +++ b/src/pages/PageLayout.styles.tsx @@ -33,22 +33,22 @@ export const styles = (theme: Theme) => marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, - paddingLeft: theme.spacing.unit * 3, - paddingRight: theme.spacing.unit * 3 + paddingLeft: theme.spacing.unit * 16, + paddingRight: theme.spacing.unit * 16, }, [theme.breakpoints.up("lg")]: { marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, - paddingLeft: theme.spacing.unit * 3, - paddingRight: theme.spacing.unit * 3 + paddingLeft: theme.spacing.unit * 32, + paddingRight: theme.spacing.unit * 32, }, [theme.breakpoints.up("xl")]: { marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, - paddingLeft: theme.spacing.unit * 3, - paddingRight: theme.spacing.unit * 3 + paddingLeft: theme.spacing.unit * 40, + paddingRight: theme.spacing.unit * 40, }, backgroundColor: theme.palette.background.default, minHeight: isDarwinApp() ? "100vh" : "auto" @@ -324,6 +324,10 @@ export const styles = (theme: Theme) => }, backgroundColor: theme.palette.primary.main }, + pageLayoutMasonry: { + paddingLeft: theme.spacing.unit * 3, + paddingRight: theme.spacing.unit * 3, + }, masonryGrid: { display: 'flex', width: 'auto', From 0b995218ea16424c7f62bf33a54729193ee8756e Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Sun, 22 Dec 2019 16:19:09 -0500 Subject: [PATCH 22/35] Update package-lock.json --- package-lock.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package-lock.json b/package-lock.json index 87bce95..1fbea6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16822,6 +16822,11 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", "dev": true }, + "react-masonry-css": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/react-masonry-css/-/react-masonry-css-1.0.14.tgz", + "integrity": "sha512-oAPVOCMApTT0HkxZJy84yU1EWaaQNZnJE0DjDMy/L+LxZoJEph4RRXsT9ppPKbFSo/tCzj+cCLwiBHjZmZ2eXA==" + }, "react-router": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz", From d67f89d6dec274d229180211849b238ecadefe57 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Sun, 22 Dec 2019 16:28:58 -0500 Subject: [PATCH 23/35] Prettify failed files --- src/pages/Home.tsx | 40 +++++++++++++++++---------------- src/pages/Local.tsx | 18 ++++++++++----- src/pages/PageLayout.styles.tsx | 19 ++++++++-------- src/pages/Public.tsx | 20 +++++++++++------ src/pages/Settings.tsx | 13 +++++------ src/utilities/settings.tsx | 4 ++-- 6 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 062e39c..3d22bc9 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -14,7 +14,7 @@ import Post from "../components/Post"; import { Status } from "../types/Status"; import Mastodon, { StreamListener } from "megalodon"; import { withSnackbar } from "notistack"; -import Masonry from 'react-masonry-css' +import Masonry from "react-masonry-css"; import { getUserDefaultBool } from "../utilities/settings"; import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; @@ -38,7 +38,7 @@ class HomePage extends Component { this.state = { viewIsLoading: true, backlogPosts: null, - isMasonryLayout: getUserDefaultBool('isMasonryLayout') + isMasonryLayout: getUserDefaultBool("isMasonryLayout") }; this.client = new Mastodon( @@ -159,9 +159,7 @@ class HomePage extends Component { render() { const { classes } = this.props; const containerClasses = `${classes.pageLayoutMaxConstraints}${ - this.state.isMasonryLayout - ? ' ' + classes.pageLayoutMasonry - : '' + this.state.isMasonryLayout ? " " + classes.pageLayoutMasonry : "" }`; return (
@@ -199,22 +197,26 @@ class HomePage extends Component { default: 4, 2000: 3, 1400: 2, - 1050: 1, + 1050: 1 }} className={classes.masonryGrid} - columnClassName={classes['my-masonry-grid_column']} - > - {this.state.posts.map((post: Status) => { - return ( -
- -
- ); - })} + columnClassName={ + classes["my-masonry-grid_column"] + } + > + {this.state.posts.map((post: Status) => { + return ( +
+ +
+ ); + })} ) : (
diff --git a/src/pages/Local.tsx b/src/pages/Local.tsx index 10094f8..258de32 100644 --- a/src/pages/Local.tsx +++ b/src/pages/Local.tsx @@ -14,7 +14,7 @@ import Post from "../components/Post"; import { Status } from "../types/Status"; import Mastodon, { StreamListener } from "megalodon"; import { withSnackbar } from "notistack"; -import Masonry from 'react-masonry-css'; +import Masonry from "react-masonry-css"; import { getUserDefaultBool } from "../utilities/settings"; import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; @@ -38,7 +38,7 @@ class LocalPage extends Component { this.state = { viewIsLoading: true, backlogPosts: null, - isMasonryLayout: getUserDefaultBool('isMasonryLayout'), + isMasonryLayout: getUserDefaultBool("isMasonryLayout") }; this.client = new Mastodon( @@ -196,14 +196,18 @@ class LocalPage extends Component { default: 4, 2000: 3, 1400: 2, - 1050: 1, + 1050: 1 }} className={classes.masonryGrid} - columnClassName={classes['my-masonry-grid_column']} + columnClassName={ + classes["my-masonry-grid_column"] + } > {this.state.posts.map((post: Status) => { return ( -
+
{
{this.state.posts.map((post: Status) => { return ( -
+
marginTop: 88, padding: theme.spacing.unit * 3, paddingLeft: theme.spacing.unit * 16, - paddingRight: theme.spacing.unit * 16, + paddingRight: theme.spacing.unit * 16 }, [theme.breakpoints.up("lg")]: { marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, paddingLeft: theme.spacing.unit * 32, - paddingRight: theme.spacing.unit * 32, + paddingRight: theme.spacing.unit * 32 }, [theme.breakpoints.up("xl")]: { marginLeft: 250, marginTop: 88, padding: theme.spacing.unit * 3, paddingLeft: theme.spacing.unit * 40, - paddingRight: theme.spacing.unit * 40, + paddingRight: theme.spacing.unit * 40 }, backgroundColor: theme.palette.background.default, minHeight: isDarwinApp() ? "100vh" : "auto" @@ -326,13 +326,14 @@ export const styles = (theme: Theme) => }, pageLayoutMasonry: { paddingLeft: theme.spacing.unit * 3, - paddingRight: theme.spacing.unit * 3, + paddingRight: theme.spacing.unit * 3 }, masonryGrid: { - display: 'flex', - width: 'auto', - }, - 'my-masonry-grid_column': { // non-standard name fixes react-masonry-css bug :shrug: - padding: 5, + display: "flex", + width: "auto" }, + "my-masonry-grid_column": { + // non-standard name fixes react-masonry-css bug :shrug: + padding: 5 + } }); diff --git a/src/pages/Public.tsx b/src/pages/Public.tsx index faa022f..1fd3106 100644 --- a/src/pages/Public.tsx +++ b/src/pages/Public.tsx @@ -14,7 +14,7 @@ import Post from "../components/Post"; import { Status } from "../types/Status"; import Mastodon, { StreamListener } from "megalodon"; import { withSnackbar } from "notistack"; -import Masonry from 'react-masonry-css'; +import Masonry from "react-masonry-css"; import { getUserDefaultBool } from "../utilities/settings"; import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward"; @@ -25,7 +25,7 @@ interface IPublicPageState { viewDidLoad?: boolean; viewDidError?: boolean; viewDidErrorCode?: any; - isMasonryLayout?:boolean; + isMasonryLayout?: boolean; } class PublicPage extends Component { @@ -38,7 +38,7 @@ class PublicPage extends Component { this.state = { viewIsLoading: true, backlogPosts: null, - isMasonryLayout: getUserDefaultBool('isMasonryLayout') + isMasonryLayout: getUserDefaultBool("isMasonryLayout") }; this.client = new Mastodon( @@ -195,14 +195,18 @@ class PublicPage extends Component { default: 4, 2000: 3, 1400: 2, - 1050: 1, + 1050: 1 }} className={classes.masonryGrid} - columnClassName={classes['my-masonry-grid_column']} + columnClassName={ + classes["my-masonry-grid_column"] + } > {this.state.posts.map((post: Status) => { return ( -
+
{
{this.state.posts.map((post: Status) => { return ( -
+
{ brandName: "Hyperspace", federated: true, imposeCharacterLimit: getUserDefaultBool("imposeCharacterLimit"), - masonryLayout: getUserDefaultBool("isMasonryLayout"), + masonryLayout: getUserDefaultBool("isMasonryLayout") }; this.toggleDarkMode = this.toggleDarkMode.bind(this); @@ -129,7 +129,7 @@ class SettingsPage extends Component { this.toggleBadgeCount = this.toggleBadgeCount.bind(this); this.toggleThemeDialog = this.toggleThemeDialog.bind(this); this.toggleVisibilityDialog = this.toggleVisibilityDialog.bind(this); - this.toggleMasonryLayout = this.toggleMasonryLayout.bind(this) + this.toggleMasonryLayout = this.toggleMasonryLayout.bind(this); this.changeThemeName = this.changeThemeName.bind(this); this.changeTheme = this.changeTheme.bind(this); this.setVisibility = this.setVisibility.bind(this); @@ -246,11 +246,8 @@ class SettingsPage extends Component { } toggleMasonryLayout() { - this.setState({ masonryLayout: !this.state.masonryLayout }) - setUserDefaultBool( - "isMasonryLayout", - !this.state.masonryLayout - ) + this.setState({ masonryLayout: !this.state.masonryLayout }); + setUserDefaultBool("isMasonryLayout", !this.state.masonryLayout); } changeTheme() { diff --git a/src/utilities/settings.tsx b/src/utilities/settings.tsx index 93be298..b71d1a2 100644 --- a/src/utilities/settings.tsx +++ b/src/utilities/settings.tsx @@ -102,7 +102,7 @@ export function createUserDefaults() { displayAllOnNotificationBadge: false, defaultVisibility: "public", imposeCharacterLimit: true, - isMasonryLayout: false, + isMasonryLayout: false }; let settings = [ @@ -112,7 +112,7 @@ export function createUserDefaults() { "displayAllOnNotificationBadge", "defaultVisibility", "imposeCharacterLimit", - "isMasonryLayout", + "isMasonryLayout" ]; migrateExistingSettings(); From 3528057e2e0c73ea02b9858f95201ae8e302aa02 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Sun, 22 Dec 2019 16:38:37 -0500 Subject: [PATCH 24/35] Apply homepage fix to local and public --- src/pages/Local.tsx | 5 ++++- src/pages/Public.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/Local.tsx b/src/pages/Local.tsx index 258de32..1faad28 100644 --- a/src/pages/Local.tsx +++ b/src/pages/Local.tsx @@ -159,9 +159,12 @@ class LocalPage extends Component { render() { const { classes } = this.props; + const containerClasses = `${classes.pageLayoutMaxConstraints}${ + this.state.isMasonryLayout ? " " + classes.pageLayoutMasonry : "" + }`; return ( -
+
{this.state.backlogPosts ? (
diff --git a/src/pages/Public.tsx b/src/pages/Public.tsx index 1fd3106..e52bc30 100644 --- a/src/pages/Public.tsx +++ b/src/pages/Public.tsx @@ -158,9 +158,12 @@ class PublicPage extends Component { render() { const { classes } = this.props; + const containerClasses = `${classes.pageLayoutMaxConstraints}${ + this.state.isMasonryLayout ? " " + classes.pageLayoutMasonry : "" + }`; return ( -
+
{this.state.backlogPosts ? (
From c0a6f3a5f9820da3ff6d2883218a45ff0dc4df9c Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 11:39:20 -0500 Subject: [PATCH 25/35] Test Windows exe upload --- .github/workflows/ci-win.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 0e82160..f41d693 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -16,4 +16,10 @@ jobs: run: | npm install npm run build --if-present - npm run build-desktop-win \ No newline at end of file + npm run build-desktop-win + - name: Upload Windows executable + uses: actions/upload-artifact@v1 + if: success() + with: + name: 'Windows executable' + path: dist/Hyperspace-*.exe \ No newline at end of file From 760ac9ddd0831535d2aeac5bc4e79263a83827a8 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 11:50:59 -0500 Subject: [PATCH 26/35] Try modifying config.json and uploading dir --- .github/workflows/ci-win.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index f41d693..41cf474 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -12,6 +12,11 @@ jobs: uses: actions/setup-node@v1 with: node-version: 10.x + - name: Modify config.json + run: | + replacer=$(cat public/config.json | grep "location") + echo $replacer + sed -i "s/$replacer/\"location\": \"desktop"/g" public/config.json; - name: Install dependencies and build run: | npm install @@ -21,5 +26,5 @@ jobs: uses: actions/upload-artifact@v1 if: success() with: - name: 'Windows executable' - path: dist/Hyperspace-*.exe \ No newline at end of file + name: 'Windows executable (output dir)' + path: dist \ No newline at end of file From 5e890347021ebc842d32e5d45c275dadac94ce26 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 11:53:08 -0500 Subject: [PATCH 27/35] Right, not a bash script --- .github/workflows/ci-win.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 41cf474..7507684 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -14,9 +14,7 @@ jobs: node-version: 10.x - name: Modify config.json run: | - replacer=$(cat public/config.json | grep "location") - echo $replacer - sed -i "s/$replacer/\"location\": \"desktop"/g" public/config.json; + sed -i "s/$(cat public/config.json | grep "location")/\"location\": \"desktop"/g" public/config.json; - name: Install dependencies and build run: | npm install From ba8183aad31138512707059e8728a0c572ee36e6 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 11:58:38 -0500 Subject: [PATCH 28/35] Change sed delimiter --- .github/workflows/ci-win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 7507684..dbc67a7 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -14,7 +14,7 @@ jobs: node-version: 10.x - name: Modify config.json run: | - sed -i "s/$(cat public/config.json | grep "location")/\"location\": \"desktop"/g" public/config.json; + sed -i "s#$(cat public/config.json | grep "location")/\"location\": \"desktop"#g" public/config.json; - name: Install dependencies and build run: | npm install From 1abf50f3a0fffdb5f76203479efbacd9b3de3f49 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 12:26:26 -0500 Subject: [PATCH 29/35] Try Powershell instead of sed? --- .github/workflows/ci-win.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index dbc67a7..631e0e8 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -14,7 +14,8 @@ jobs: node-version: 10.x - name: Modify config.json run: | - sed -i "s#$(cat public/config.json | grep "location")/\"location\": \"desktop"#g" public/config.json; + ((Get-Content -Path .\public\config,json -Raw) -replace '/\"location\"?: \"https?:\/\/([A-Za-z0-9.]+)\",/g','\"location\": \"desktop\",') | Set-Content -Path .\public\config.json + shell: powershell - name: Install dependencies and build run: | npm install From f35f3ef9dc941dd5d79326ec9fe11e17fc3b620d Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 12:27:45 -0500 Subject: [PATCH 30/35] Fix file path issue --- .github/workflows/ci-win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 631e0e8..81c6444 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -14,7 +14,7 @@ jobs: node-version: 10.x - name: Modify config.json run: | - ((Get-Content -Path .\public\config,json -Raw) -replace '/\"location\"?: \"https?:\/\/([A-Za-z0-9.]+)\",/g','\"location\": \"desktop\",') | Set-Content -Path .\public\config.json + ((Get-Content -Path .\public\config.json -Raw) -replace '/\"location\"?: \"https?:\/\/([A-Za-z0-9.]+)\",/g','\"location\": \"desktop\",') | Set-Content -Path .\public\config.json shell: powershell - name: Install dependencies and build run: | From 877d2512fcb1d0ee053ab1087101de63d22f3497 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 14:03:56 -0500 Subject: [PATCH 31/35] Try using Python instead --- .github/workflows/ci-win.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 81c6444..d42a545 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -12,10 +12,20 @@ jobs: uses: actions/setup-node@v1 with: node-version: 10.x - - name: Modify config.json + - name: Change desktop field run: | - ((Get-Content -Path .\public\config.json -Raw) -replace '/\"location\"?: \"https?:\/\/([A-Za-z0-9.]+)\",/g','\"location\": \"desktop\",') | Set-Content -Path .\public\config.json - shell: powershell + from json import load, dump + + json_dict = {} + with open('public/config.json', 'r') as file: + json_dict = load(file) + + json_dict["location"] = "desktop" + + with open('public/config.json', 'w+') as out: + dump(json_dict, out) + + shell: python - name: Install dependencies and build run: | npm install From bf4cdd6d8c433a6c7a28abac4f7fc8f33e4094d6 Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 14:56:45 -0500 Subject: [PATCH 32/35] Try verifying changes --- .github/workflows/ci-win.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index d42a545..e58e2ce 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -24,8 +24,10 @@ jobs: with open('public/config.json', 'w+') as out: dump(json_dict, out) - shell: python + - name: Verify changes + run: (Get-Content -Path .\public\config.json -Raw) -match "location" + uses: powershell - name: Install dependencies and build run: | npm install From 3de449949413ee45d309f34065f39543945e19bf Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 14:57:42 -0500 Subject: [PATCH 33/35] Fix uses and use shell --- .github/workflows/ci-win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index e58e2ce..ff56907 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -27,7 +27,7 @@ jobs: shell: python - name: Verify changes run: (Get-Content -Path .\public\config.json -Raw) -match "location" - uses: powershell + shell: powershell - name: Install dependencies and build run: | npm install From a95d06664d0b20ec44f1db5d713b99a00c59f3da Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 14:59:08 -0500 Subject: [PATCH 34/35] Do cat-and-grp instead --- .github/workflows/ci-win.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index ff56907..2c49b9b 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -26,8 +26,7 @@ jobs: dump(json_dict, out) shell: python - name: Verify changes - run: (Get-Content -Path .\public\config.json -Raw) -match "location" - shell: powershell + run: cat public/config.json | grep 'location' - name: Install dependencies and build run: | npm install From a1f8c209b70af00783965bb369758838dd2bde7f Mon Sep 17 00:00:00 2001 From: Marquis Kurt Date: Mon, 23 Dec 2019 15:02:45 -0500 Subject: [PATCH 35/35] Remove change verfication --- .github/workflows/ci-win.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci-win.yml b/.github/workflows/ci-win.yml index 2c49b9b..17b1736 100644 --- a/.github/workflows/ci-win.yml +++ b/.github/workflows/ci-win.yml @@ -25,8 +25,6 @@ jobs: with open('public/config.json', 'w+') as out: dump(json_dict, out) shell: python - - name: Verify changes - run: cat public/config.json | grep 'location' - name: Install dependencies and build run: | npm install