Merge pull request #1066 from h3poteto/iss-982

refs #982 Load proxy information and apply for all network connection
This commit is contained in:
AkiraFukushima 2019-10-27 21:08:11 +09:00 committed by GitHub
commit cf646069a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 883 additions and 149 deletions

View File

@ -0,0 +1,60 @@
import path from 'path'
import ProxyConfiguration from '~/src/main/proxy'
import { ManualProxy, ProxyProtocol } from '~/src/types/proxy'
const preferencesDBPath = path.resolve(__dirname, '../../preferences.json')
const proxyConfiguration = new ProxyConfiguration(preferencesDBPath)
describe('Parser', () => {
it('do not use proxy', () => {
proxyConfiguration.setSystemProxy('DIRECT')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).toEqual(false)
})
it('HTTP and HTTPS proxy', () => {
proxyConfiguration.setSystemProxy('PROXY hoge.example.com:8080')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).not.toBe(false)
const manualProxy = proxy as ManualProxy
expect(manualProxy.protocol).toEqual(ProxyProtocol.http)
expect(manualProxy.host).toEqual('hoge.example.com')
expect(manualProxy.port).toEqual('8080')
})
it('SOCKS4 proxy', () => {
proxyConfiguration.setSystemProxy('SOCKS4 hoge.example.com:8080')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).not.toBe(false)
const manualProxy = proxy as ManualProxy
expect(manualProxy.protocol).toEqual(ProxyProtocol.socks4)
})
it('SOCKS4A proxy', () => {
proxyConfiguration.setSystemProxy('SOCKS4A hoge.example.com:8080')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).not.toBe(false)
const manualProxy = proxy as ManualProxy
expect(manualProxy.protocol).toEqual(ProxyProtocol.socks4a)
})
it('SOCKS5 proxy', () => {
proxyConfiguration.setSystemProxy('SOCKS5 hoge.example.com:8080')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).not.toBe(false)
const manualProxy = proxy as ManualProxy
expect(manualProxy.protocol).toEqual(ProxyProtocol.socks5)
})
it('SOCKS5H proxy', () => {
proxyConfiguration.setSystemProxy('SOCKS5H hoge.example.com:8080')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).not.toBe(false)
const manualProxy = proxy as ManualProxy
expect(manualProxy.protocol).toEqual(ProxyProtocol.socks5h)
})
it('SOCKS proxy', () => {
proxyConfiguration.setSystemProxy('SOCKS hoge.example.com:8080')
const proxy = proxyConfiguration.parseSystemProxy()
expect(proxy).not.toBe(false)
const manualProxy = proxy as ManualProxy
expect(manualProxy.protocol).toEqual(ProxyProtocol.socks5)
})
})

1
spec/preferences.json Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -22,6 +22,13 @@ const initStore = () => {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Login', () => {
let store
let localVue
@ -31,7 +38,8 @@ describe('Login', () => {
localVue.use(Vuex)
store = new Vuex.Store({
modules: {
Login: initStore()
Login: initStore(),
App: appState
}
})
})

View File

@ -112,6 +112,13 @@ const initStore = () => {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('TimelineSpace', () => {
let store
let localVue
@ -121,7 +128,8 @@ describe('TimelineSpace', () => {
localVue.use(Vuex)
store = new Vuex.Store({
modules: {
TimelineSpace: initStore()
TimelineSpace: initStore(),
App: appState
}
})
})

View File

@ -135,6 +135,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Home', () => {
let store
let localVue
@ -145,7 +152,8 @@ describe('Home', () => {
store = new Vuex.Store({
modules: {
DirectMessages: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -133,6 +133,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Favourites', () => {
let store
let localVue
@ -143,7 +150,8 @@ describe('Favourites', () => {
store = new Vuex.Store({
modules: {
Favourites: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -81,6 +81,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Home', () => {
let store
let localVue
@ -91,7 +98,8 @@ describe('Home', () => {
store = new Vuex.Store({
modules: {
FollowRequests: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -120,6 +120,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Home', () => {
let store
let localVue
@ -130,7 +137,8 @@ describe('Home', () => {
store = new Vuex.Store({
modules: {
Tag: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -121,6 +121,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Home', () => {
let store
let localVue
@ -131,7 +138,8 @@ describe('Home', () => {
store = new Vuex.Store({
modules: {
Home: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -54,6 +54,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Lists/Edit', () => {
let store
let localVue
@ -64,7 +71,8 @@ describe('Lists/Edit', () => {
store = new Vuex.Store({
modules: {
Edit: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -36,6 +36,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Lists/Index', () => {
let store
let localVue
@ -46,7 +53,8 @@ describe('Lists/Index', () => {
store = new Vuex.Store({
modules: {
Index: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -122,6 +122,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Lists/Show', () => {
let store
let localVue
@ -132,7 +139,8 @@ describe('Lists/Show', () => {
store = new Vuex.Store({
modules: {
Show: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -119,6 +119,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Home', () => {
let store
let localVue
@ -129,7 +136,8 @@ describe('Home', () => {
store = new Vuex.Store({
modules: {
Local: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -121,6 +121,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Mentions', () => {
let store
let localVue
@ -131,7 +138,8 @@ describe('Mentions', () => {
store = new Vuex.Store({
modules: {
Mentions: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -191,6 +191,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Notifications', () => {
let store
let localVue
@ -201,7 +208,8 @@ describe('Notifications', () => {
store = new Vuex.Store({
modules: {
Notifications: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -119,6 +119,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Home', () => {
let store
let localVue
@ -129,7 +136,8 @@ describe('Home', () => {
store = new Vuex.Store({
modules: {
Public: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -65,6 +65,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Search/Account', () => {
let store
let localVue
@ -75,7 +82,8 @@ describe('Search/Account', () => {
store = new Vuex.Store({
modules: {
Account: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -48,6 +48,12 @@ const timelineState = {
}
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Search/Account', () => {
let store
@ -59,7 +65,8 @@ describe('Search/Account', () => {
store = new Vuex.Store({
modules: {
Tag: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -97,6 +97,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('Search/Account', () => {
let store
let localVue
@ -107,7 +114,8 @@ describe('Search/Account', () => {
store = new Vuex.Store({
modules: {
Toots: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -38,6 +38,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('HeaderMenu', () => {
let store
let localVue
@ -48,7 +55,8 @@ describe('HeaderMenu', () => {
store = new Vuex.Store({
modules: {
HeaderMenu: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -54,6 +54,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('AddListMember', () => {
let store
let localVue
@ -64,7 +71,8 @@ describe('AddListMember', () => {
store = new Vuex.Store({
modules: {
AddListMember: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -65,6 +65,13 @@ const timelineState = {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('ListMembership', () => {
let store
let localVue
@ -75,7 +82,8 @@ describe('ListMembership', () => {
store = new Vuex.Store({
modules: {
ListMembership: initStore(),
TimelineSpace: timelineState
TimelineSpace: timelineState,
App: appState
}
})
})

View File

@ -42,6 +42,13 @@ const initStore = () => {
}
}
const appState = {
namespaced: true,
state: {
proxyConfiguration: false
}
}
describe('SideMenu', () => {
let store
let localVue
@ -51,7 +58,8 @@ describe('SideMenu', () => {
localVue.use(Vuex)
store = new Vuex.Store({
modules: {
SideMenu: initStore()
SideMenu: initStore(),
App: appState
}
})
mockedMegalodon.mockClear()

View File

@ -1,5 +1,5 @@
import { isEmpty } from 'lodash'
import Mastodon, { Account as RemoteAccount } from 'megalodon'
import Mastodon, { Account as RemoteAccount, ProxyConfig } from 'megalodon'
import Datastore from 'nedb'
import log from 'electron-log'
import { LocalAccount } from '~/src/types/localAccount'
@ -275,14 +275,14 @@ export default class Account {
return updated
}
async refreshAccounts(): Promise<Array<LocalAccount>> {
async refreshAccounts(proxy: ProxyConfig | false): Promise<Array<LocalAccount>> {
const accounts = await this.listAccounts()
if (accounts.length < 1) {
return accounts
}
const results = await Promise.all(
accounts.map(async account => {
const refresh = await this.refresh(account)
const refresh = await this.refresh(account, proxy)
return refresh
})
)
@ -294,8 +294,8 @@ export default class Account {
* @param {LocalAccount} account is an local account
* @return {LocalAccount} updated account
*/
async refresh(account: LocalAccount): Promise<LocalAccount> {
let client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1')
async refresh(account: LocalAccount, proxy: ProxyConfig | false): Promise<LocalAccount> {
let client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1', 'Whalebird', proxy)
let json = {}
try {
const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
@ -311,8 +311,8 @@ export default class Account {
if (!account.refreshToken) {
throw new RefreshTokenDoesNotExist()
}
const token = await Mastodon.refreshToken(account.clientId, account.clientSecret, account.refreshToken, account.baseURL)
client = new Mastodon(token.access_token, account.baseURL + '/api/v1')
const token = await Mastodon.refreshToken(account.clientId, account.clientSecret, account.refreshToken, account.baseURL, proxy)
client = new Mastodon(token.access_token, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
json = {
username: res.data.username,
@ -326,8 +326,8 @@ export default class Account {
}
// Confirm the access token, and check duplicate
async fetchAccount(account: LocalAccount, accessToken: string): Promise<RemoteAccount> {
const client = new Mastodon(accessToken, account.baseURL + '/api/v1')
async fetchAccount(account: LocalAccount, accessToken: string, proxy: ProxyConfig | false): Promise<RemoteAccount> {
const client = new Mastodon(accessToken, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
const query = {
baseURL: account.baseURL,

View File

@ -1,4 +1,4 @@
import Mastodon, { OAuth } from 'megalodon'
import Mastodon, { OAuth, ProxyConfig } from 'megalodon'
import Account from './account'
import { LocalAccount } from '~/src/types/localAccount'
@ -30,7 +30,7 @@ export default class Authentication {
this.clientSecret = ''
}
async getAuthorizationUrl(domain = 'mastodon.social'): Promise<string> {
async getAuthorizationUrl(domain = 'mastodon.social', proxy: ProxyConfig | false): Promise<string> {
this.setOtherInstance(domain)
const res = await Mastodon.registerApp(
appName,
@ -38,7 +38,8 @@ export default class Authentication {
scopes: scope,
website: appURL
},
this.baseURL
this.baseURL,
proxy
)
this.clientId = res.clientId
this.clientSecret = res.clientSecret
@ -69,8 +70,15 @@ export default class Authentication {
return res.url
}
async getAccessToken(code: string): Promise<string> {
const tokenData: OAuth.TokenData = await Mastodon.fetchAccessToken(this.clientId, this.clientSecret, code, this.baseURL)
async getAccessToken(code: string, proxy: ProxyConfig | false): Promise<string> {
const tokenData: OAuth.TokenData = await Mastodon.fetchAccessToken(
this.clientId,
this.clientSecret,
code,
this.baseURL,
'urn:ietf:wg:oauth:2.0:oob',
proxy
)
const search = {
baseURL: this.baseURL,
domain: this.domain,
@ -80,7 +88,7 @@ export default class Authentication {
const rec = await this.db.searchAccount(search)
const accessToken = tokenData.accessToken
const refreshToken = tokenData.refreshToken
const data = await this.db.fetchAccount(rec, accessToken)
const data = await this.db.fetchAccount(rec, accessToken, proxy)
await this.db.updateAccount(rec._id!, {
username: data.username,
accountId: data.id,

View File

@ -4,6 +4,7 @@ import {
app,
ipcMain,
shell,
session,
Menu,
Tray,
BrowserWindow,
@ -45,6 +46,7 @@ import HashtagCache from './cache/hashtag'
import AccountCache from './cache/account'
import { InsertAccountCache } from '~/src/types/insertAccountCache'
import { Proxy } from '~/src/types/proxy'
import ProxyConfiguration from './proxy'
/**
* Context menu
@ -144,6 +146,7 @@ const soundBasePath =
process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../build/sounds/') : path.join(process.resourcesPath!, 'build/sounds/')
let launcher: AutoLaunch | null = null
const proxyConfiguration = new ProxyConfiguration(preferencesDBPath)
// On MAS build, auto launch is not working.
// We have to use Launch Agent: https://github.com/Teamwork/node-auto-launch/issues/43
@ -270,6 +273,16 @@ async function createWindow() {
mainWindow.webContents.on('will-navigate', event => event.preventDefault())
/**
* Get system proxy configuration.
*/
if (session && session.defaultSession) {
session.defaultSession.resolveProxy('https://mastodon.social', proxyInfo => {
proxyConfiguration.setSystemProxy(proxyInfo)
log.info(`System proxy configuration: ${proxyInfo}`)
})
}
mainWindow.on('closed', () => {
mainWindow = null
})
@ -336,9 +349,10 @@ app.on('activate', () => {
let auth = new Authentication(accountManager)
ipcMain.on('get-auth-url', (event: IpcMainEvent, domain: string) => {
ipcMain.on('get-auth-url', async (event: IpcMainEvent, domain: string) => {
const proxy = await proxyConfiguration.forMastodon()
auth
.getAuthorizationUrl(domain)
.getAuthorizationUrl(domain, proxy)
.then(url => {
log.debug(url)
event.sender.send('response-get-auth-url', url)
@ -351,9 +365,10 @@ ipcMain.on('get-auth-url', (event: IpcMainEvent, domain: string) => {
})
})
ipcMain.on('get-access-token', (event: IpcMainEvent, code: string) => {
ipcMain.on('get-access-token', async (event: IpcMainEvent, code: string) => {
const proxy = await proxyConfiguration.forMastodon()
auth
.getAccessToken(code)
.getAccessToken(code, proxy)
.then(token => {
accountDB.findOne(
{
@ -406,9 +421,10 @@ ipcMain.on('get-local-account', (event: IpcMainEvent, id: string) => {
})
})
ipcMain.on('update-account', (event: IpcMainEvent, acct: LocalAccount) => {
ipcMain.on('update-account', async (event: IpcMainEvent, acct: LocalAccount) => {
const proxy = await proxyConfiguration.forMastodon()
accountManager
.refresh(acct)
.refresh(acct, proxy)
.then(ac => {
event.sender.send('response-update-account', ac)
})
@ -452,9 +468,10 @@ ipcMain.on('backward-account', (event: IpcMainEvent, acct: LocalAccount) => {
})
})
ipcMain.on('refresh-accounts', (event: IpcMainEvent) => {
ipcMain.on('refresh-accounts', async (event: IpcMainEvent) => {
const proxy = await proxyConfiguration.forMastodon()
accountManager
.refreshAccounts()
.refreshAccounts(proxy)
.then(accounts => {
event.sender.send('response-refresh-accounts', accounts)
})
@ -510,8 +527,9 @@ ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<Lo
userStreamings[id]!.stop()
userStreamings[id] = null
}
const url = await StreamingURL(acct)
userStreamings[id] = new WebSocket(acct, url)
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
userStreamings[id] = new WebSocket(acct, url, proxy)
userStreamings[id]!.startUserStreaming(
async (update: Status) => {
if (!event.sender.isDestroyed()) {
@ -620,9 +638,9 @@ ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, obj: St
directMessagesStreaming.stop()
directMessagesStreaming = null
}
const url = await StreamingURL(acct)
directMessagesStreaming = new WebSocket(acct, url)
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
directMessagesStreaming = new WebSocket(acct, url, proxy)
directMessagesStreaming.start(
'direct',
(update: Status) => {
@ -669,9 +687,9 @@ ipcMain.on('start-local-streaming', async (event: IpcMainEvent, obj: StreamingSe
localStreaming.stop()
localStreaming = null
}
const url = await StreamingURL(acct)
localStreaming = new WebSocket(acct, url)
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
localStreaming = new WebSocket(acct, url, proxy)
localStreaming.start(
'public:local',
(update: Status) => {
@ -718,9 +736,9 @@ ipcMain.on('start-public-streaming', async (event: IpcMainEvent, obj: StreamingS
publicStreaming.stop()
publicStreaming = null
}
const url = await StreamingURL(acct)
publicStreaming = new WebSocket(acct, url)
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
publicStreaming = new WebSocket(acct, url, proxy)
publicStreaming.start(
'public',
(update: Status) => {
@ -771,9 +789,9 @@ ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListID & Str
listStreaming.stop()
listStreaming = null
}
const url = await StreamingURL(acct)
listStreaming = new WebSocket(acct, url)
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
listStreaming = new WebSocket(acct, url, proxy)
listStreaming.start(
`list&list=${listID}`,
(update: Status) => {
@ -824,9 +842,9 @@ ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: Tag & Streami
tagStreaming.stop()
tagStreaming = null
}
const url = await StreamingURL(acct)
tagStreaming = new WebSocket(acct, url)
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
tagStreaming = new WebSocket(acct, url, proxy)
tagStreaming.start(
`hashtag&tag=${tag}`,
(update: Status) => {
@ -982,6 +1000,11 @@ ipcMain.on('update-proxy-config', (event: IpcMainEvent, proxy: Proxy) => {
})
})
ipcMain.on('get-proxy-configuration', async (event: IpcMainEvent) => {
const proxy = await proxyConfiguration.forMastodon()
event.sender.send('response-get-proxy-configuration', proxy)
})
// language
ipcMain.on('change-language', (event: IpcMainEvent, value: string) => {
const preferences = new Preferences(preferencesDBPath)

View File

@ -1,4 +1,5 @@
import storage from 'electron-json-storage'
import log from 'electron-log'
import objectAssignDeep from 'object-assign-deep'
import DisplayStyle from '../constants/displayStyle'
import Theme from '../constants/theme'
@ -97,6 +98,7 @@ export default class Preferences {
const preferences = await this.get()
return objectAssignDeep({}, Base, preferences)
} catch (err) {
log.error(err)
return Base
}
}

104
src/main/proxy.ts Normal file
View File

@ -0,0 +1,104 @@
import { ProxyConfig } from 'megalodon'
import { ProxySource, ManualProxy, ProxyProtocol } from '~/src/types/proxy'
import Preferences from './preferences'
export default class ProxyConfiguration {
public preferences: Preferences
public systemProxy: string | null = null
constructor(preferencesDBPath: string) {
this.preferences = new Preferences(preferencesDBPath)
}
public setSystemProxy(proxy: string) {
this.systemProxy = proxy
}
public async forMastodon(): Promise<false | ProxyConfig> {
const proxy = await this.getConfig()
if (!proxy) {
return false
} else {
let protocol = ProxyProtocol.http
if (proxy.protocol !== '') {
protocol = proxy.protocol
}
if (proxy.username.length > 0) {
return {
host: proxy.host,
port: parseInt(proxy.port, 10),
protocol: protocol,
auth: {
username: proxy.username,
password: proxy.password
}
}
} else {
return {
host: proxy.host,
port: parseInt(proxy.port, 10),
protocol: protocol
}
}
}
}
public async getConfig(): Promise<false | ManualProxy> {
const conf = await this.preferences.get()
const source = conf.proxy.source as ProxySource
switch (source) {
case ProxySource.no:
return false
case ProxySource.system:
if (this.systemProxy) {
return this.parseSystemProxy()
} else {
return false
}
case ProxySource.manual:
return conf.proxy.manualProxyConfig
}
}
public parseSystemProxy(): ManualProxy | false {
if (!this.systemProxy) {
return false
}
if (this.systemProxy === 'DIRECT') {
return false
}
const result = this.systemProxy.match(/^([A-Z0-9]+)\s+([a-z0-9-_.]+):([0-9]+)$/)
if (!result || result.length !== 4) {
return false
}
let protocol = ProxyProtocol.http
switch (result[1]) {
case 'PROXY':
protocol = ProxyProtocol.http
break
case 'SOCKS4':
protocol = ProxyProtocol.socks4
break
case 'SOCKS4A':
protocol = ProxyProtocol.socks4a
break
case 'SOCKS5':
protocol = ProxyProtocol.socks5
break
case 'SOCKS5H':
protocol = ProxyProtocol.socks5h
break
case 'SOCKS':
protocol = ProxyProtocol.socks5
break
}
const manual: ManualProxy = {
protocol: protocol,
host: result[2],
port: result[3],
username: '',
password: ''
}
return manual
}
}

View File

@ -1,12 +1,12 @@
import Mastodon, { WebSocket as SocketListener, Status, Notification, Instance, Response } from 'megalodon'
import Mastodon, { WebSocket as SocketListener, Status, Notification, Instance, Response, ProxyConfig } from 'megalodon'
import log from 'electron-log'
import { LocalAccount } from '~/src/types/localAccount'
const StreamingURL = async (account: LocalAccount): Promise<string> => {
const StreamingURL = async (account: LocalAccount, proxy: ProxyConfig | false): Promise<string> => {
if (!account.accessToken) {
throw new Error('access token is empty')
}
const client = new Mastodon(account.accessToken, account.baseURL + '/api/v1')
const client = new Mastodon(account.accessToken, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res: Response<Instance> = await client.get<Instance>('/instance')
return res.data.urls.streaming_api
}
@ -17,9 +17,9 @@ export default class WebSocket {
private client: Mastodon
private listener: SocketListener | null
constructor(account: LocalAccount, streamingURL: string) {
constructor(account: LocalAccount, streamingURL: string, proxy: ProxyConfig | false) {
const url = streamingURL.replace(/^https:\/\//, 'wss://')
this.client = new Mastodon(account.accessToken!, url + '/api/v1')
this.client = new Mastodon(account.accessToken!, url + '/api/v1', 'Whalebird', proxy)
this.listener = null
}

View File

@ -36,6 +36,7 @@ export default {
this.$store.dispatch('App/loadPreferences').then(conf => {
this.$i18n.i18next.changeLanguage(conf.language.language)
})
this.$store.dispatch('App/loadProxy')
},
destroyed() {
this.$store.dispatch('App/removeShortcutsEvents')

View File

@ -10,7 +10,8 @@ import DefaultFonts from '@/utils/fonts'
import { RootState } from '@/store'
import { Notify } from '~/src/types/notify'
import { BaseConfig } from '~/src/types/preference'
import { Appearance } from '~src/types/appearance'
import { Appearance } from '~/src/types/appearance'
import { ProxyConfig } from 'megalodon'
export type AppState = {
theme: ThemeColorType
@ -24,6 +25,8 @@ export type AppState = {
ignoreNFSW: boolean
hideAllAttachments: boolean
tootPadding: number
proxyConfiguration: ProxyConfig | false
userAgent: string
}
const state = (): AppState => ({
@ -42,7 +45,9 @@ const state = (): AppState => ({
defaultFonts: DefaultFonts,
ignoreCW: false,
ignoreNFSW: false,
hideAllAttachments: false
hideAllAttachments: false,
proxyConfiguration: false,
userAgent: 'Whalebird'
})
const MUTATION_TYPES = {
@ -56,7 +61,8 @@ const MUTATION_TYPES = {
ADD_FONT: 'addFont',
UPDATE_IGNORE_CW: 'updateIgnoreCW',
UPDATE_IGNORE_NFSW: 'updateIgnoreNFSW',
UPDATE_HIDE_ALL_ATTACHMENTS: 'updateHideAllAttachments'
UPDATE_HIDE_ALL_ATTACHMENTS: 'updateHideAllAttachments',
UPDATE_PROXY_CONFIGURATION: 'updateProxyConfiguration'
}
const mutations: MutationTree<AppState> = {
@ -93,6 +99,9 @@ const mutations: MutationTree<AppState> = {
},
[MUTATION_TYPES.UPDATE_HIDE_ALL_ATTACHMENTS]: (state: AppState, hideAllAttachments: boolean) => {
state.hideAllAttachments = hideAllAttachments
},
[MUTATION_TYPES.UPDATE_PROXY_CONFIGURATION]: (state, proxy: ProxyConfig | false) => {
state.proxyConfiguration = proxy
}
}
@ -154,6 +163,15 @@ const actions: ActionTree<AppState, RootState> = {
commit(MUTATION_TYPES.UPDATE_THEME, LightTheme)
break
}
},
loadProxy: ({ commit }) => {
return new Promise(resolve => {
ipcRenderer.once('response-get-proxy-configuration', (_, proxy: ProxyConfig | false) => {
commit(MUTATION_TYPES.UPDATE_PROXY_CONFIGURATION, proxy)
resolve(proxy)
})
ipcRenderer.send('get-proxy-configuration')
})
}
}

View File

@ -1,5 +1,5 @@
import { ipcRenderer } from 'electron'
import axios from 'axios'
import Mastodon from 'megalodon'
import { Module, MutationTree, ActionTree } from 'vuex'
import { RootState } from '@/store'
@ -44,11 +44,11 @@ const actions: ActionTree<LoginState, RootState> = {
pageBack: ({ commit }) => {
commit(MUTATION_TYPES.CHANGE_INSTANCE, null)
},
confirmInstance: async ({ commit }, domain: string): Promise<boolean> => {
confirmInstance: async ({ commit, rootState }, domain: string): Promise<boolean> => {
commit(MUTATION_TYPES.CHANGE_SEARCHING, true)
// https://gist.github.com/okapies/60d62d0df0163bbfb4ab09c1766558e8
// Check /.well-known/host-meta to confirm mastodon instance.
const res = await axios.get(`https://${domain}/.well-known/host-meta`).finally(() => {
const res = await Mastodon.get<any>('/.well-known/host-meta', {}, `https://${domain}`, rootState.App.proxyConfiguration).finally(() => {
commit(MUTATION_TYPES.CHANGE_SEARCHING, false)
})
const parser = new DOMParser()

View File

@ -29,7 +29,12 @@ const mutations: MutationTree<GeneralState> = {
const actions: ActionTree<GeneralState, RootState> = {
fetchSettings: async ({ commit, rootState }): Promise<Account> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res = await client.get<Account>('/accounts/verify_credentials')
const visibility: VisibilityType | undefined = (Object.values(Visibility) as Array<VisibilityType>).find(v => {
return v.key === res.data.source!.privacy
@ -39,7 +44,12 @@ const actions: ActionTree<GeneralState, RootState> = {
return res.data
},
setVisibility: async ({ commit, rootState }, value: number) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const visibility: VisibilityType | undefined = (Object.values(Visibility) as Array<VisibilityType>).find(v => {
return v.value === value
})
@ -52,7 +62,12 @@ const actions: ActionTree<GeneralState, RootState> = {
return res.data
},
setSensitive: async ({ commit, rootState }, value: boolean) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res = await client.patch<Account>('/accounts/update_credentials', {
source: {
sensitive: value

View File

@ -161,8 +161,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
commit(MUTATION_TYPES.UPDATE_ACCOUNT, blankAccount)
return true
},
detectPleroma: async ({ commit, state }) => {
const res = await Mastodon.get<Instance>('/instance', {}, state.account.baseURL + '/api/v1')
detectPleroma: async ({ commit, state, rootState }) => {
const res = await Mastodon.get<Instance>('/instance', {}, state.account.baseURL + '/api/v1', rootState.App.proxyConfiguration)
if (res.data.version.includes('Pleroma')) {
commit(MUTATION_TYPES.CHANGE_PLEROMA, true)
} else {
@ -194,16 +194,16 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
/**
* fetchEmojis
*/
fetchEmojis: async ({ commit }, account: LocalAccount): Promise<Array<Emoji>> => {
const res = await Mastodon.get<Array<Emoji>>('/custom_emojis', {}, account.baseURL + '/api/v1')
fetchEmojis: async ({ commit, rootState }, account: LocalAccount): Promise<Array<Emoji>> => {
const res = await Mastodon.get<Array<Emoji>>('/custom_emojis', {}, account.baseURL + '/api/v1', rootState.App.proxyConfiguration)
commit(MUTATION_TYPES.UPDATE_EMOJIS, res.data)
return res.data
},
/**
* fetchInstance
*/
fetchInstance: async ({ commit }, account: LocalAccount) => {
const res = await Mastodon.get<Instance>('/instance', {}, account.baseURL + '/api/v1')
fetchInstance: async ({ commit, rootState }, account: LocalAccount) => {
const res = await Mastodon.get<Instance>('/instance', {}, account.baseURL + '/api/v1', rootState.App.proxyConfiguration)
commit(MUTATION_TYPES.UPDATE_TOOT_MAX, res.data.max_toot_chars)
return true
},

View File

@ -96,7 +96,12 @@ const mutations: MutationTree<DirectMessagesState> = {
const actions: ActionTree<DirectMessagesState, RootState> = {
fetchTimeline: async ({ commit, rootState }): Promise<Array<Status>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Conversation>> = await client.get<Array<Conversation>>('/conversations', { limit: 40 })
const statuses: Array<Status> = res.data.map(con => con.last_status!)
commit(MUTATION_TYPES.UPDATE_TIMELINE, statuses)
@ -107,7 +112,12 @@ const actions: ActionTree<DirectMessagesState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Conversation>>('/conversations', { max_id: lastStatus.id, limit: 40 })
.then(res => {

View File

@ -72,8 +72,13 @@ const mutations: MutationTree<FavouritesState> = {
}
const actions: ActionTree<FavouritesState, RootState> = {
fetchFavourites: async ({ commit }, account: LocalAccount): Promise<Array<Status>> => {
const client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1')
fetchFavourites: async ({ commit, rootState }, account: LocalAccount): Promise<Array<Status>> => {
const client = new Mastodon(
account.accessToken!,
account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>('/favourites', { limit: 40 })
commit(MUTATION_TYPES.UPDATE_FAVOURITES, res.data)
// Parse link header
@ -98,7 +103,12 @@ const actions: ActionTree<FavouritesState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>('/favourites', { max_id: state.maxId, limit: 40 }).finally(() => {
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, false)
})

View File

@ -22,20 +22,35 @@ const mutations: MutationTree<FollowRequestsState> = {
const actions: ActionTree<FollowRequestsState, RootState> = {
fetchRequests: async ({ commit, rootState }): Promise<Array<Account>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>('/follow_requests')
commit(MUTATION_TYPES.UPDATE_REQUESTS, res.data)
return res.data
},
acceptRequest: async ({ dispatch, rootState }, user: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<{}> = await client.post<{}>(`/follow_requests/${user.id}/authorize`)
await dispatch('fetchRequests')
dispatch('TimelineSpace/SideMenu/fetchFollowRequests', rootState.TimelineSpace.account, { root: true })
return res.data
},
rejectRequest: async ({ dispatch, rootState }, user: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<{}> = await client.post<{}>(`/follow_requests/${user.id}/reject`)
await dispatch('fetchRequests')
dispatch('TimelineSpace/SideMenu/fetchFollowRequests', rootState.TimelineSpace.account, { root: true })

View File

@ -97,7 +97,12 @@ const mutations: MutationTree<TagState> = {
const actions: ActionTree<TagState, RootState> = {
fetch: async ({ commit, rootState }, tag: string): Promise<Array<Status>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>(`/timelines/tag/${encodeURIComponent(tag)}`, { limit: 40 })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data
@ -138,7 +143,12 @@ const actions: ActionTree<TagState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Status>>(`/timelines/tag/${loadPosition.tag}`, { max_id: loadPosition.status.id, limit: 40 })
.then(res => {

View File

@ -108,7 +108,12 @@ const mutations: MutationTree<HomeState> = {
const actions: ActionTree<HomeState, RootState> = {
fetchTimeline: async ({ commit, rootState }) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>('/timelines/home', { limit: 40 })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data
@ -118,7 +123,12 @@ const actions: ActionTree<HomeState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Status>>('/timelines/home', { max_id: lastStatus.id, limit: 40 })
.then(res => {

View File

@ -23,7 +23,12 @@ const mutations: MutationTree<EditState> = {
const actions: ActionTree<EditState, RootState> = {
fetchMembers: async ({ commit, rootState }, listId: string): Promise<Array<Account>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>(`/lists/${listId}/accounts`, {
limit: 0
})
@ -31,7 +36,12 @@ const actions: ActionTree<EditState, RootState> = {
return res.data
},
removeAccount: async ({ rootState }, remove: RemoveAccountFromList): Promise<{}> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client.del<{}>(`/lists/${remove.listId}/accounts`, {
account_ids: [remove.account.id]
})

View File

@ -22,13 +22,23 @@ const mutations: MutationTree<IndexState> = {
const actions: ActionTree<IndexState, RootState> = {
fetchLists: async ({ commit, rootState }): Promise<Array<List>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<List>> = await client.get<Array<List>>('/lists')
commit(MUTATION_TYPES.CHANGE_LISTS, res.data)
return res.data
},
createList: async ({ rootState }, title: string): Promise<List> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<List> = await client.post<List>('/lists', {
title: title
})

View File

@ -97,7 +97,12 @@ const mutations: MutationTree<ShowState> = {
const actions: ActionTree<ShowState, RootState> = {
fetchTimeline: async ({ commit, rootState }, listID: string): Promise<Array<Status>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>(`/timelines/list/${listID}`, { limit: 40 })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data
@ -138,7 +143,12 @@ const actions: ActionTree<ShowState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Status>>(`/timelines/list/${loadPosition.list_id}`, { max_id: loadPosition.status.id, limit: 40 })
.then(res => {

View File

@ -95,7 +95,12 @@ const mutations: MutationTree<LocalState> = {
const actions: ActionTree<LocalState, RootState> = {
fetchLocalTimeline: async ({ commit, rootState }): Promise<Array<Status>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>('/timelines/public', { limit: 40, local: true })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data
@ -105,7 +110,12 @@ const actions: ActionTree<LocalState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Status>>('/timelines/public', { max_id: lastStatus.id, limit: 40, local: true })
.then(res => {

View File

@ -95,7 +95,12 @@ const mutations: MutationTree<MentionsState> = {
const actions: ActionTree<MentionsState, RootState> = {
fetchMentions: async ({ commit, rootState }): Promise<Array<Notification>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Notification>> = await client.get<Array<Notification>>('/notifications', {
limit: 30,
exclude_types: ['follow', 'favourite', 'reblog']
@ -108,7 +113,12 @@ const actions: ActionTree<MentionsState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Notification>>('/notifications', { max_id: lastMention.id, limit: 30, exclude_types: ['follow', 'favourite', 'reblog'] })
.then(res => {

View File

@ -97,7 +97,12 @@ const mutations: MutationTree<NotificationsState> = {
const actions: ActionTree<NotificationsState, RootState> = {
fetchNotifications: async ({ commit, rootState }): Promise<Array<Notification>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Notification>> = await client.get<Array<Notification>>('/notifications', { limit: 30 })
commit(MUTATION_TYPES.UPDATE_NOTIFICATIONS, res.data)
@ -108,7 +113,12 @@ const actions: ActionTree<NotificationsState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Notification>>('/notifications', { max_id: lastNotification.id, limit: 30 })
.then(res => {

View File

@ -95,7 +95,12 @@ const mutations: MutationTree<PublicState> = {
const actions: ActionTree<PublicState, RootState> = {
fetchPublicTimeline: async ({ commit, rootState }): Promise<Array<Status>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Status>> = await client.get<Array<Status>>('/timelines/public', { limit: 40 })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data
@ -105,7 +110,12 @@ const actions: ActionTree<PublicState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Status>>('/timelines/public', { max_id: lastStatus.id, limit: 40 })
.then(res => {

View File

@ -23,7 +23,12 @@ const mutations: MutationTree<AccountState> = {
const actions: ActionTree<AccountState, RootState> = {
search: async ({ commit, rootState }, query: string): Promise<Array<Account>> => {
commit('TimelineSpace/Contents/changeLoading', true, { root: true })
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Array<Account>>('/accounts/search', { q: query, resolve: true })
.then(res => {

View File

@ -23,7 +23,12 @@ const mutations: MutationTree<TagState> = {
const actions: ActionTree<TagState, RootState> = {
search: ({ commit, rootState }, query: string): Promise<Array<Tag>> => {
commit('TimelineSpace/Contents/changeLoading', true, { root: true })
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v2')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v2',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Results>('/search', { q: query, resolve: true })
.then(res => {

View File

@ -23,7 +23,12 @@ const mutations: MutationTree<TootsState> = {
const actions: ActionTree<TootsState, RootState> = {
search: ({ commit, rootState }, query: string): Promise<Array<Status>> => {
commit('TimelineSpace/Contents/changeLoading', true, { root: true })
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.get<Results>('/search', { q: query, resolve: true })
.then(res => {

View File

@ -45,12 +45,22 @@ const mutations: MutationTree<AccountProfileState> = {
const actions: ActionTree<AccountProfileState, RootState> = {
fetchAccount: async ({ rootState }, accountID: string): Promise<Account> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Account> = await client.get<Account>(`/accounts/${accountID}`)
return res.data
},
searchAccount: async ({ rootState }, parsedAccount): Promise<Account> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>('/accounts/search', { q: parsedAccount.url, resolve: true })
if (res.data.length <= 0) throw new AccountNotFound('empty result')
const account = res.data.find(a => `@${a.acct}` === parsedAccount.acct)
@ -69,7 +79,12 @@ const actions: ActionTree<AccountProfileState, RootState> = {
},
fetchRelationship: async ({ commit, rootState }, account: Account): Promise<Relationship> => {
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, null)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.get<Relationship>('/accounts/relationships', { id: [account.id] })
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, res.data[0])
return res.data
@ -86,14 +101,24 @@ const actions: ActionTree<AccountProfileState, RootState> = {
})
},
follow: async ({ commit, rootState, dispatch }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.post<Relationship>(`/accounts/${account.id}/follow`)
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, res.data)
dispatch('fetchRelationship', account)
return res.data
},
unfollow: async ({ commit, rootState, dispatch }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.post<Relationship>(`/accounts/${account.id}/unfollow`)
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, res.data)
dispatch('fetchRelationship', account)
@ -103,21 +128,36 @@ const actions: ActionTree<AccountProfileState, RootState> = {
commit(MUTATION_TYPES.CHANGE_ACCOUNT, null)
},
unmute: async ({ rootState, commit, dispatch }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.post<Relationship>(`/accounts/${account.id}/unmute`)
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, res.data)
dispatch('fetchRelationship', account)
return res.data
},
block: async ({ rootState, commit, dispatch }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.post<Relationship>(`/accounts/${account.id}/block`)
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, res.data)
dispatch('fetchRelationship', account)
return res.data
},
unblock: async ({ rootState, commit, dispatch }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.post<Relationship>(`/accounts/${account.id}/unblock`)
commit(MUTATION_TYPES.CHANGE_RELATIONSHIP, res.data)
dispatch('fetchRelationship', account)

View File

@ -28,14 +28,24 @@ const mutations: MutationTree<FollowersState> = {
const actions: ActionTree<FollowersState, RootState> = {
fetchFollowers: async ({ commit, rootState }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>(`/accounts/${account.id}/followers`, { limit: 80 })
commit(MUTATION_TYPES.UPDATE_FOLLOWERS, res.data)
return res.data
},
fetchRelationships: async ({ commit, rootState }, accounts: Array<Account>) => {
const ids = accounts.map(a => a.id)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Relationship>> = await client.get<Array<Relationship>>(`/accounts/relationships`, {
id: ids
})

View File

@ -28,14 +28,24 @@ const mutations: MutationTree<FollowsState> = {
const actions: ActionTree<FollowsState, RootState> = {
fetchFollows: async ({ commit, rootState }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>(`/accounts/${account.id}/following`, { limit: 80 })
commit(MUTATION_TYPES.UPDATE_FOLLOWS, res.data)
return res.data
},
fetchRelationships: async ({ commit, rootState }, accounts: Array<Account>) => {
const ids = accounts.map(a => a.id)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Relationship>> = await client.get<Array<Relationship>>(`/accounts/relationships`, {
id: ids
})

View File

@ -85,7 +85,12 @@ const mutations: MutationTree<TimelineState> = {
const actions: ActionTree<TimelineState, RootState> = {
fetchTimeline: async ({ commit, rootState }, account: Account) => {
commit('TimelineSpace/Contents/SideBar/AccountProfile/changeLoading', true, { root: true })
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const pinned: Response<Array<Status>> = await client.get<Array<Status>>(`/accounts/${account.id}/statuses`, { limit: 10, pinned: true })
commit(MUTATION_TYPES.UPDATE_PINNED_TOOTS, pinned.data)
const res: Response<Array<Status>> = await client.get<Array<Status>>(`/accounts/${account.id}/statuses`, { limit: 40 })
@ -98,7 +103,12 @@ const actions: ActionTree<TimelineState, RootState> = {
return Promise.resolve(null)
}
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
try {
const res: Response<Array<Status>> = await client.get<Array<Status>>(`/accounts/${loadPosition.account.id}/statuses`, {
max_id: loadPosition.status.id,

View File

@ -120,7 +120,12 @@ const actions: ActionTree<TootDetailState, RootState> = {
commit(MUTATION_TYPES.CHANGE_TOOT, message)
},
fetchToot: async ({ commit, rootState }, message: Status) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Context> = await client.get<Context>(`/statuses/${message.id}/context`, { limit: 40 })
commit(MUTATION_TYPES.UPDATE_ANCESTORS, res.data.ancestors)

View File

@ -35,7 +35,12 @@ const mutations: MutationTree<HeaderMenuState> = {
const actions: ActionTree<HeaderMenuState, RootState> = {
fetchList: async ({ commit, rootState }, listID: string): Promise<List> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<List> = await client.get<List>(`/lists/${listID}`)
commit(MUTATION_TYPES.UPDATE_TITLE, `#${res.data.title}`)
return res.data

View File

@ -37,7 +37,12 @@ const actions: ActionTree<AddListMemberState, RootState> = {
commit(MUTATION_TYPES.CHANGE_MODAL, value)
},
search: async ({ commit, rootState }, name: string): Promise<Array<Account>> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>('/accounts/search', {
q: name,
following: true
@ -46,7 +51,12 @@ const actions: ActionTree<AddListMemberState, RootState> = {
return res.data
},
add: async ({ state, rootState }, account: Account): Promise<{}> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<{}> = await client.post<{}>(`/lists/${state.targetListId}/accounts`, {
account_ids: [account.id]
})

View File

@ -47,13 +47,23 @@ const actions: ActionTree<ListMembershipState, RootState> = {
commit(MUTATION_TYPES.CHANGE_ACCOUNT, account)
},
fetchListMembership: async ({ commit, rootState }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<List>> = await client.get<Array<List>>(`/accounts/${account.id}/lists`)
commit(MUTATION_TYPES.CHANGE_BELONG_TO_LISTS, res.data.map(l => l.id))
return res.data
},
fetchLists: async ({ commit, rootState }) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<List>> = await client.get<Array<List>>('/lists')
commit(MUTATION_TYPES.CHANGE_LISTS, res.data)
return res.data
@ -63,7 +73,12 @@ const actions: ActionTree<ListMembershipState, RootState> = {
const removedLists = lodash.difference(state.belongToLists, belongToLists)
const addedLists = lodash.difference(belongToLists, state.belongToLists)
commit(MUTATION_TYPES.CHANGE_BELONG_TO_LISTS, belongToLists)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const removedPromise = removedLists.map(id => {
return client.del<{}>(`/lists/${id}/accounts`, {
account_ids: [state.account!.id]

View File

@ -34,7 +34,12 @@ const actions: ActionTree<MuteConfirmState, RootState> = {
commit(MUTATION_TYPES.CHANGE_ACCOUNT, account)
},
submit: async ({ state, rootState, dispatch }, notify: boolean) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Relationship> = await client.post<Relationship>(`/accounts/${state.account!.id}/mute`, {
notifications: notify
})

View File

@ -172,7 +172,12 @@ const actions: ActionTree<NewTootState, RootState> = {
if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) {
throw new AuthenticationError()
}
const client = new Mastodon(rootState.TimelineSpace.account.accessToken, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const attachments = Object.keys(mediaDescription).map(async id => {
if (mediaDescription[id] !== null) {
return client.put<Attachment>(`/media/${id}`, { description: mediaDescription[id] })
@ -253,7 +258,12 @@ const actions: ActionTree<NewTootState, RootState> = {
}
commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, true)
const client = new Mastodon(rootState.TimelineSpace.account.accessToken, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client
.post<Status>('/statuses', form)
.then((res: Response<Status>) => {
@ -305,7 +315,12 @@ const actions: ActionTree<NewTootState, RootState> = {
if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) {
throw new AuthenticationError()
}
const client = new Mastodon(rootState.TimelineSpace.account.accessToken, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const formData = new FormData()
formData.append('file', image)
return client
@ -342,7 +357,12 @@ const actions: ActionTree<NewTootState, RootState> = {
}
},
fetchVisibility: async ({ commit, rootState }) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Account> = await client.get<Account>('/accounts/verify_credentials')
const visibility: VisibilityType | undefined = (Object.values(Visibility) as Array<VisibilityType>).find(v => {
return v.key === res.data.source!.privacy

View File

@ -165,7 +165,12 @@ const actions: ActionTree<StatusState, RootState> = {
})
}
const searchAPI = async () => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
commit(MUTATION_TYPES.SET_CLIENT, client)
const res: Response<Array<Account>> = await client.get<Array<Account>>('/accounts/search', { q: word, resolve: false })
if (res.data.length === 0) throw new Error('Empty')
@ -204,7 +209,12 @@ const actions: ActionTree<StatusState, RootState> = {
})
}
const searchAPI = async () => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v2')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v2',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
commit(MUTATION_TYPES.SET_CLIENT, client)
const res: Response<Results> = await client.get<Results>('/search', { q: word })
if (res.data.hashtags.length === 0) throw new Error('Empty')

View File

@ -32,7 +32,12 @@ const actions: ActionTree<ReportState, RootState> = {
commit(MUTATION_TYPES.CHANGE_MODAL_OPEN, true)
},
submit: async ({ rootState }, { account_id, status_id, comment }) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client.post<{}>(`/reports`, {
account_id: account_id,
status_ids: [status_id],

View File

@ -80,14 +80,24 @@ const mutations: MutationTree<SideMenuState> = {
const actions: ActionTree<SideMenuState, RootState> = {
fetchLists: async ({ commit, rootState }, account: LocalAccount | null = null): Promise<Array<List>> => {
if (account === null) account = rootState.TimelineSpace.account
const client = new Mastodon(account!.accessToken!, account!.baseURL + '/api/v1')
const client = new Mastodon(
account!.accessToken!,
account!.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<List>> = await client.get<Array<List>>('/lists')
commit(MUTATION_TYPES.UPDATE_LISTS, res.data)
return res.data
},
fetchFollowRequests: async ({ commit, rootState }, account: LocalAccount | null = null): Promise<Array<Account>> => {
if (account === null) account = rootState.TimelineSpace.account
const client = new Mastodon(account!.accessToken!, account!.baseURL + '/api/v1')
const client = new Mastodon(
account!.accessToken!,
account!.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Array<Account>> = await client.get<Array<Account>>('/follow_requests')
commit(MUTATION_TYPES.CHANGE_UNREAD_FOLLOW_REQUESTS, res.data.length > 0)
return res.data

View File

@ -14,7 +14,12 @@ const state = (): TootState => ({})
const actions: ActionTree<TootState, RootState> = {
reblog: async ({ rootState, dispatch }, message: Status) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/reblog`)
// API returns new status when reblog.
// Reblog target status is in the data.reblog.
@ -24,42 +29,77 @@ const actions: ActionTree<TootState, RootState> = {
return res.data.reblog
},
unreblog: async ({ rootState, dispatch }, message: Status) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/unreblog`)
dispatch('TimelineSpace/updateTootForAllTimelines', res.data, { root: true })
return res.data
},
addFavourite: async ({ rootState, dispatch }, message: Status) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/favourite`)
ipcRenderer.send('fav-rt-action-sound')
dispatch('TimelineSpace/updateTootForAllTimelines', res.data, { root: true })
return res.data
},
removeFavourite: async ({ rootState, dispatch }, message: Status) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/unfavourite`)
dispatch('TimelineSpace/updateTootForAllTimelines', res.data, { root: true })
return res.data
},
deleteToot: async ({ rootState }, message: Status) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
await client.del(`/statuses/${message.id}`)
return message
},
block: async ({ rootState }, account: Account) => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
return client.post(`/accounts/${account.id}/block`)
},
vote: async ({ rootState }, params: VoteParam): Promise<Poll> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res = await client.post<Poll>(`/polls/${params.id}/votes`, {
choices: params.choices
})
return res.data
},
refresh: async ({ rootState }, id: string): Promise<Poll> => {
const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
const client = new Mastodon(
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1',
rootState.App.userAgent,
rootState.App.proxyConfiguration
)
const res = await client.get<Poll>(`/polls/${id}`)
return res.data
}