diff --git a/main-electron.js b/main-electron.js index 32f628da..7398cae6 100644 --- a/main-electron.js +++ b/main-electron.js @@ -9,166 +9,181 @@ const fs = require("fs"); let win; function createWindow() { - // Create the browser window. - win = new BrowserWindow({ - width: 377, - height: 800, - title: "Sengi", - backgroundColor: "#FFF", - useContentSize: true - }); + // Create the browser window. + win = new BrowserWindow({ + width: 377, + height: 800, + title: "Sengi", + backgroundColor: "#FFF", + useContentSize: true + }); - var server = http.createServer(requestHandler).listen(9527); - win.loadURL("http://localhost:9527"); + var server = http.createServer(requestHandler).listen(9527); + win.loadURL("http://localhost:9527"); - const template = [ - { - label: "View", - submenu: [ - { role: "reload" }, - { role: "forcereload" }, - { type: "separator" }, - { role: "close" }, - { role: 'quit' } - ] - }, - { - role: "help", - submenu: [ - { role: "toggledevtools" }, + const template = [ { - label: "Open GitHub project", - click() { - require("electron").shell.openExternal( - "https://github.com/NicolasConstant/sengi" - ); - } + label: "View", + submenu: [ + { role: "reload" }, + { role: "forcereload" }, + { type: "separator" }, + { role: "close" }, + { role: 'quit' } + ] + }, + { + role: "help", + submenu: [ + { role: "toggledevtools" }, + { + label: "Open GitHub project", + click() { + require("electron").shell.openExternal( + "https://github.com/NicolasConstant/sengi" + ); + } + } + ] } - ] + ]; + + const menu = Menu.buildFromTemplate(template); + win.setMenu(menu); + + // Check if we are on a MAC + if (process.platform === "darwin") { + // Create our menu entries so that we can use MAC shortcuts + Menu.setApplicationMenu( + Menu.buildFromTemplate([ + { + label: "Edit", + submenu: [ + { role: "undo" }, + { role: "redo" }, + { type: "separator" }, + { role: "cut" }, + { role: "copy" }, + { role: "paste" }, + { role: "pasteandmatchstyle" }, + { role: "delete" }, + { role: "selectall" }, + { type: "separator" }, + { role: "close" }, + { role: 'quit' } + ] + }, + { + label: "View", + submenu: [{ role: "reload" }, { role: "forcereload" }] + }, + { + role: "help", + submenu: [ + { role: "toggledevtools" }, + { + label: "Open GitHub project", + click() { + require("electron").shell.openExternal( + "https://github.com/NicolasConstant/sengi" + ); + } + } + ] + } + ]) + ); } - ]; - const menu = Menu.buildFromTemplate(template); - win.setMenu(menu); + // Open the DevTools. + // win.webContents.openDevTools() - // Check if we are on a MAC - if (process.platform === "darwin") { - // Create our menu entries so that we can use MAC shortcuts - Menu.setApplicationMenu( - Menu.buildFromTemplate([ - { - label: "Edit", - submenu: [ - { role: "undo" }, - { role: "redo" }, - { type: "separator" }, - { role: "cut" }, - { role: "copy" }, - { role: "paste" }, - { role: "pasteandmatchstyle" }, - { role: "delete" }, - { role: "selectall" }, - { type: "separator" }, - { role: "close" }, - { role: 'quit' } - ] - }, - { - label: "View", - submenu: [{ role: "reload" }, { role: "forcereload" }] - }, - { - role: "help", - submenu: [ - { role: "toggledevtools" }, - { - label: "Open GitHub project", - click() { - require("electron").shell.openExternal( - "https://github.com/NicolasConstant/sengi" - ); - } - } - ] - } - ]) - ); - } + //open external links to browser + win.webContents.on("new-window", function (event, url) { + event.preventDefault(); + shell.openExternal(url); + }); - // Open the DevTools. - // win.webContents.openDevTools() - - //open external links to browser - win.webContents.on("new-window", function(event, url) { - event.preventDefault(); - shell.openExternal(url); - }); - - // Emitted when the window is closed. - win.on("closed", () => { - // Dereference the window object, usually you would store windows - // in an array if your app supports multi windows, this is the time - // when you should delete the corresponding element. - win = null; - }); + // Emitted when the window is closed. + win.on("closed", () => { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + win = null; + }); } function requestHandler(req, res) { - var file = req.url == "/" ? "/index.html" : req.url, - root = __dirname + "/dist", - page404 = root + "/404.html"; + var file = req.url == "/" ? "/index.html" : req.url, + root = __dirname + "/dist", + page404 = root + "/404.html"; - if (file.includes("register") || file.includes("home")) file = "/index.html"; + if (file.includes("register") || file.includes("home")) file = "/index.html"; - getFile(root + file, res, page404); + getFile(root + file, res, page404); } function getFile(filePath, res, page404) { - console.warn(`filePath: ${filePath}`); - fs.exists(filePath, function(exists) { - if (exists) { - fs.readFile(filePath, function(err, contents) { - if (!err) { - res.end(contents); + console.warn(`filePath: ${filePath}`); + fs.exists(filePath, function (exists) { + if (exists) { + fs.readFile(filePath, function (err, contents) { + if (!err) { + res.end(contents); + } else { + console.dir(err); + } + }); } else { - console.dir(err); + fs.readFile(page404, function (err, contents) { + if (!err) { + res.writeHead(404, { "Content-Type": "text/html" }); + res.end(contents); + } else { + console.dir(err); + } + }); } - }); - } else { - fs.readFile(page404, function(err, contents) { - if (!err) { - res.writeHead(404, { "Content-Type": "text/html" }); - res.end(contents); - } else { - console.dir(err); - } - }); - } - }); + }); } app.commandLine.appendSwitch("force-color-profile", "srgb"); -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on("ready", createWindow); + +const gotTheLock = app.requestSingleInstanceLock(); + +if (!gotTheLock) { + app.quit(); +} else { + app.on('second-instance', (event, commandLine, workingDirectory) => { + // Someone tried to run a second instance, we should focus our window. + if (win) { + if (win.isMinimized()) win.restore() + win.focus() + } + }); + + // This method will be called when Electron has finished + // initialization and is ready to create browser windows. + // Some APIs can only be used after this event occurs. + app.on("ready", createWindow); +} // Quit when all windows are closed. app.on("window-all-closed", () => { - // On macOS it is common for applications and their menu bar - // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== "darwin") { - app.quit(); - } + // On macOS it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== "darwin") { + app.quit(); + } }); app.on("activate", () => { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (win === null) { - createWindow(); - } + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (win === null) { + createWindow(); + } }); // In this file you can include the rest of your app's specific main process diff --git a/package.json b/package.json index 055beb6e..89524caa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sengi", - "version": "0.10.1", + "version": "0.11.0", "license": "AGPL-3.0-or-later", "main": "main-electron.js", "description": "A multi-account desktop client for Mastodon and Pleroma", diff --git a/src/app/components/create-status/create-status.component.ts b/src/app/components/create-status/create-status.component.ts index cab9d3a2..ee706dc3 100644 --- a/src/app/components/create-status/create-status.component.ts +++ b/src/app/components/create-status/create-status.component.ts @@ -20,7 +20,14 @@ import { identifierModuleUrl } from '@angular/compiler'; styleUrls: ['./create-status.component.scss'] }) export class CreateStatusComponent implements OnInit, OnDestroy { - title: string; + private _title: string; + set title(value: string){ + this._title = value; + this.countStatusChar(this.status); + } + get title(): string { + return this._title; + } private _status: string = ''; set status(value: string) { @@ -106,14 +113,21 @@ export class CreateStatusComponent implements OnInit, OnDestroy { private accountChanged(accounts: AccountInfo[]): void { if (accounts && accounts.length > 0) { const selectedAccount = accounts.filter(x => x.isSelected)[0]; - this.instancesInfoService.getMaxStatusChars(selectedAccount.instance) - .then((maxChars: number) => { - this.maxCharLength = maxChars; - this.countStatusChar(this.status); - }) - .catch((err: HttpErrorResponse) => { - this.notificationService.notifyHttpError(err); - }); + + const settings = this.toolsService.getAccountSettings(selectedAccount); + if (settings.customStatusCharLengthEnabled) { + this.maxCharLength = settings.customStatusCharLength; + this.countStatusChar(this.status); + } else { + this.instancesInfoService.getMaxStatusChars(selectedAccount.instance) + .then((maxChars: number) => { + this.maxCharLength = maxChars; + this.countStatusChar(this.status); + }) + .catch((err: HttpErrorResponse) => { + this.notificationService.notifyHttpError(err); + }); + } if (!this.statusReplyingToWrapper) { this.instancesInfoService.getDefaultPrivacy(selectedAccount) @@ -169,10 +183,18 @@ export class CreateStatusComponent implements OnInit, OnDestroy { const statusExtraChars = this.getMentionExtraChars(status); const statusLength = currentStatus.length - statusExtraChars; - this.charCountLeft = this.maxCharLength - statusLength; + this.charCountLeft = this.maxCharLength - statusLength - this.getCwLength(); this.postCounts = parseStatus.length; } + private getCwLength(): number { + let cwLength = 0; + if (this.title) { + cwLength = this.title.length; + } + return cwLength; + } + private getMentions(status: Status, providerInfo: AccountInfo): string[] { const mentions = [...status.mentions.map(x => x.acct), status.account.acct]; @@ -238,9 +260,6 @@ export class CreateStatusComponent implements OnInit, OnDestroy { return this.sendStatus(acc, this.status, visibility, this.title, status, mediaAttachments); }) .then((res: Status) => { - if (this.statusReplyingToWrapper) { - this.notificationService.newStatusPosted(this.statusReplyingToWrapper.status.id, new StatusWrapper(res, acc)); - } this.title = ''; this.status = ''; this.onClose.emit(); @@ -261,22 +280,30 @@ export class CreateStatusComponent implements OnInit, OnDestroy { for (let i = 0; i < parsedStatus.length; i++) { let s = parsedStatus[i]; - resultPromise = resultPromise.then((pStatus: Status) => { - let inReplyToId = null; - if (pStatus) { - inReplyToId = pStatus.id; - } + resultPromise = resultPromise + .then((pStatus: Status) => { + let inReplyToId = null; + if (pStatus) { + inReplyToId = pStatus.id; + } - if (i === 0) { - return this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, attachments.map(x => x.id)) - .then((status: Status) => { - this.mediaService.clearMedia(); - return status; - }); - } else { - return this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, []); - } - }); + if (i === 0) { + return this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, attachments.map(x => x.id)) + .then((status: Status) => { + this.mediaService.clearMedia(); + return status; + }); + } else { + return this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, []); + } + }) + .then((status: Status) => { + if (this.statusReplyingToWrapper) { + this.notificationService.newStatusPosted(this.statusReplyingToWrapper.status.id, new StatusWrapper(status, account)); + } + + return status; + }); } return resultPromise; @@ -293,7 +320,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy { aggregateMention += `${x} `; }); - const currentMaxCharLength = this.maxCharLength + mentionExtraChars; + const currentMaxCharLength = this.maxCharLength + mentionExtraChars - this.getCwLength(); const maxChars = currentMaxCharLength - 6; while (trucatedStatus.length > currentMaxCharLength) { diff --git a/src/app/components/floating-column/manage-account/manage-account.component.html b/src/app/components/floating-column/manage-account/manage-account.component.html index 37314bfb..b6e92d85 100644 --- a/src/app/components/floating-column/manage-account/manage-account.component.html +++ b/src/app/components/floating-column/manage-account/manage-account.component.html @@ -2,7 +2,9 @@

Manage Account

- + + + - @@ -27,27 +28,18 @@
- - - - - - + \ No newline at end of file diff --git a/src/app/components/floating-column/manage-account/manage-account.component.ts b/src/app/components/floating-column/manage-account/manage-account.component.ts index 9bd9ff5b..58a66a69 100644 --- a/src/app/components/floating-column/manage-account/manage-account.component.ts +++ b/src/app/components/floating-column/manage-account/manage-account.component.ts @@ -6,6 +6,10 @@ import { Subscription } from 'rxjs'; import { AccountWrapper } from '../../../models/account.models'; import { UserNotificationService, UserNotification } from '../../../services/user-notification.service'; import { OpenThreadEvent } from '../../../services/tools.service'; +import { MastodonService } from '../../../services/mastodon.service'; +import { Account } from "../../../services/models/mastodon.interfaces"; +import { NotificationService } from '../../../services/notification.service'; +import { AccountInfo } from '../../../states/accounts.state'; @Component({ @@ -25,6 +29,8 @@ export class ManageAccountComponent implements OnInit, OnDestroy { hasNotifications = false; hasMentions = false; + userAccount: Account; + @Output() browseAccountEvent = new EventEmitter(); @Output() browseHashtagEvent = new EventEmitter(); @Output() browseThreadEvent = new EventEmitter(); @@ -33,6 +39,7 @@ export class ManageAccountComponent implements OnInit, OnDestroy { set account(acc: AccountWrapper) { this._account = acc; this.checkNotifications(); + this.getUserUrl(acc.info); } get account(): AccountWrapper { return this._account; @@ -42,6 +49,8 @@ export class ManageAccountComponent implements OnInit, OnDestroy { private _account: AccountWrapper; constructor( + private readonly mastodonService: MastodonService, + private readonly notificationService: NotificationService, private readonly userNotificationService: UserNotificationService) { } ngOnInit() { @@ -52,6 +61,16 @@ export class ManageAccountComponent implements OnInit, OnDestroy { this.userNotificationServiceSub.unsubscribe(); } + private getUserUrl(account: AccountInfo){ + this.mastodonService.retrieveAccountDetails(this.account.info) + .then((acc: Account) => { + this.userAccount = acc; + }) + .catch(err => { + this.notificationService.notifyHttpError(err); + }); + } + private checkNotifications(){ if(this.userNotificationServiceSub){ this.userNotificationServiceSub.unsubscribe(); @@ -75,6 +94,17 @@ export class ManageAccountComponent implements OnInit, OnDestroy { this.browseAccountEvent.next(accountName); } + browseLocalAccount(): boolean { + var accountName = `@${this.account.info.username}@${this.account.info.instance}`; + this.browseAccountEvent.next(accountName); + return false; + } + + openLocalAccount(): boolean { + window.open(this.userAccount.url, '_blank'); + return false; + } + browseHashtag(hashtag: string): void { this.browseHashtagEvent.next(hashtag); } diff --git a/src/app/components/floating-column/manage-account/my-account/my-account.component.html b/src/app/components/floating-column/manage-account/my-account/my-account.component.html index ced4c965..a8ef56e8 100644 --- a/src/app/components/floating-column/manage-account/my-account/my-account.component.html +++ b/src/app/components/floating-column/manage-account/my-account/my-account.component.html @@ -9,21 +9,21 @@