Skip to content

Commit

Permalink
- Changed webPreferences.webSecurity: false, so fetch() can be used f…
Browse files Browse the repository at this point in the history
…rom the renderer process.

- Added new component: ComponentFunds.vue that will handle listing of fund information from Finansinspektionen webpage.
- Added API: fiAPI.js - holds objects for Finansinspektionen inofficial API
- Added dependency: unzip - to be able to unpack zip files downloaded from Finansinspektionen
- Added dependency: x2js - to be able to convert XML files from the downloaded zip to JSON objects.
- Added dependency: localforage - to be able to use IndexedDB with a simple(r) API.
- Moved API: ibindexAPI.js up one folder and removed ibindex folder from api folder.
  • Loading branch information
PeterBlenessy committed Jan 28, 2022
1 parent a354e30 commit c669df0
Show file tree
Hide file tree
Showing 17 changed files with 800 additions and 286 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0


## [Unreleased]
- Changed webPreferences.webSecurity: false, so fetch() can be used from the renderer process.
- Added new component: ComponentFunds.vue that will handle listing of fund information from Finansinspektionen webpage.
- Added API: fiAPI.js - holds objects for Finansinspektionen inofficial API
- Added dependency: unzip - to be able to unpack zip files downloaded from Finansinspektionen
- Added dependency: x2js - to be able to convert XML files from the downloaded zip to JSON objects.
- Added dependency: localforage - to be able to use IndexedDB with a simple(r) API.
- Moved API: ibindexAPI.js up one folder and removed ibindex folder from api folder.

- Removed dependency: axios and migrated components to use fetch() instead.
- Cleaned up electron-preload.js and electron-main.js from axios code.

## v0.5.3 - 2022-01-17
- Upgraded Electron to v16.0.7.
- Removed not-implemented fetch-request from preload.js.

## v0.5.2 - 2021-12-03
- Added package.json script - git:push - to do add, commit and push changes.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ yarn

### Start the app in development mode (hot-code reloading, error reporting, etc.)
```bash
quasar dev
yarn dev
```

### Lint the files
Expand Down
104 changes: 104 additions & 0 deletions USECASES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Stoqster

## Application features

- [x] Sidebar with menu items
- [x] Toolbar with icons
- [x] Tooltip
- [x] Dark/light mode
- [x] In app notifications
- [x] Automatic updates
- [x] Persisted state

## Display information fetched from www.ibindex.se
Ibindex is a web page presenting information about investment companies in Sweden.

- [x] Searchable table showing information about investment companies, e.g., net asset value, rebate/premium.
- [x] Expand/hide list of investment companies rows and display additional information, e.g., company holdings, event calendar.
- [x] Dashboard of cards with selected investment companies, showing the calculated rebate/premium, and an expandable list of the historical values. Persisted selection.
- [x] Set/delete alarm on the dashboard: current value crossing 30 days' average. Persisted alarms.
- [x] Searchable table showing market weights of investment companies.
- [x] Display notification when refresh of data is done.
- [x] Display notification when alarm ir triggered.

## Display information fetched from www.fi.se

### Latest quarterly fund holdings
Fund managers report their holdings to Finansinspektionen, Sweden's financial supervisory authority, on a quarterly basis and holdings is available for download as a zip archive of XML files at https://www.fi.se/sv/vara-register/fondinnehav-per-kvartal/

- [x] Display fund information in a table. Searchable.
- [] Selectable visible columns.
- [] Display fund holdings.

#### Flowchart

```mermaid
flowchart LR
%% Definition of the different elements in the flowchart
onMounted(onMounted)
onRefresh(onRefresh)
refresh(refresh data )
fetch("download \n fetch()")
blob("response.blob() \n get blob \n ")
unzip(unzip)
getxml("entry.text() \n get one xml file \n ")
json("xml2json() \n convert \n one xml file \n to json")
files[(files)]
dB[("dataBase")]
%% The main loop
subgraph main
direction LR
onMounted
onRefresh
refresh
end
%% The fetch flow
subgraph fetchZip
direction TB
fetch --> blob --> unzip --> getxml --> json
end
subgraph IndexedDB
direction TB
files
dB
end
%% How much time is spent in the different blocks
fetch -- "400 ms" .-fetch
blob -- "3 000 ms" .-blob
unzip -- "10 ms" .-unzip
getxml -- "0.2 - 2 ms" .-getxml
json -- "0.2 - 8 ms" .-json
%% Function calls
onMounted --> refresh
onRefresh --> refresh
refresh --> fetchZip
%% Read/write operations
%%blob -. "write" .-> files
%%unzip -. "read" .-> files
%%getxml -. "read" .-> dB
%%json -. "write" .-> dB
```


## Display information fetched from Placera news feed
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stoqster",
"version": "0.5.2",
"version": "0.5.4",
"description": "Follow your favourite stocks and fonds, and your portfolio's performance",
"productName": "Stoqster",
"author": "Péter Blénessy",
Expand All @@ -22,9 +22,12 @@
"core-js": "^3.6.5",
"electron-log": "^4.4.1",
"electron-updater": "^4.6.1",
"localforage": "^1.10.0",
"quasar": "^2.4.10",
"unzipit": "^1.4.0",
"vue-i18n": "^9.0.0",
"vuex": "^4.0.1"
"vuex": "^4.0.1",
"x2js": "^3.4.3"
},
"devDependencies": {
"@babel/eslint-parser": "^7.13.14",
Expand Down
181 changes: 107 additions & 74 deletions src-electron/electron-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,100 +5,133 @@ import path from 'path'
import axios from 'axios'

try {
if (process.platform === 'win32' && nativeTheme.shouldUseDarkColors === true) {
require('fs').unlinkSync(require('path').join(app.getPath('userData'), 'DevTools Extensions'))
}
if (process.platform === 'win32' && nativeTheme.shouldUseDarkColors === true) {
require('fs').unlinkSync(require('path').join(app.getPath('userData'), 'DevTools Extensions'))
}
} catch (_) { }

let mainWindow

function createWindow () {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 1200,
minHeight: 800,
useContentSize: true,
webPreferences: {
contextIsolation: true,
// More info: /quasar-cli/developing-electron-apps/electron-preload-script
preload: path.resolve(__dirname, process.env.QUASAR_ELECTRON_PRELOAD)
function createWindow() {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 1200,
minHeight: 800,
useContentSize: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
// By disabling webSecurity, it is possible to run fetch() in the browser.
// Since we only load data from known sources, and no HTML or JS, the risk is kind of under control.
webSecurity: false,
// More info: /quasar-cli/developing-electron-apps/electron-preload-script
preload: path.resolve(__dirname, process.env.QUASAR_ELECTRON_PRELOAD)
}
})

mainWindow.loadURL(process.env.APP_URL)

if (process.env.DEBUGGING) {
// if on DEV or Production with debug enabled
// mainWindow.webContents.openDevTools()
} else {
// we're on production; no access to devtools pls
mainWindow.webContents.on('devtools-opened', () => {
// mainWindow.webContents.closeDevTools()
})
}
})

mainWindow.loadURL(process.env.APP_URL)
// Check for updates
mainWindow.once('ready-to-show', () => {
autoUpdater.logger = logger;
autoUpdater.checkForUpdatesAndNotify();
logger.info('registered-auto-update');

if (process.env.DEBUGGING) {
// if on DEV or Production with debug enabled
// mainWindow.webContents.openDevTools()
} else {
// we're on production; no access to devtools pls
mainWindow.webContents.on('devtools-opened', () => {
// mainWindow.webContents.closeDevTools()
setInterval(() => {
autoUpdater.checkForUpdatesAndNotify();
logger.info('registered-auto-update refresh interval');
}, 1000 * 60 * 60); // Check every hour
});

mainWindow.on('closed', () => {
mainWindow = null
})
}

// Check for updates
mainWindow.once('ready-to-show', () => {
autoUpdater.logger = logger;
autoUpdater.checkForUpdatesAndNotify();
logger.info('registered-auto-update');

setInterval(() => {
autoUpdater.checkForUpdatesAndNotify();
logger.info('registered-auto-update refresh interval');
}, 1000 * 60 * 60); // Check every hour
});

mainWindow.on('closed', () => {
mainWindow = null
})
}

app.on('ready', createWindow)

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
if (process.platform !== 'darwin') {
app.quit()
}
})

app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
if (mainWindow === null) {
createWindow()
}
})

// IPC handler for generic axios get/post requests
// axios_options is the json object with
ipcMain.handle('axios-request', async (_, options) => {

try {
let response = await axios.request(options);
let data;

if (response.config.responseType === 'arraybuffer') {
// We need to handle special charactesr such as åäöÅÄÖ
data = JSON.parse(response.data.toString('latin1'));
} else {
data = response.data;
}

// IPC does not allow you to directly return promises,
// you can only return basic types and objects that can be serializable.
return {
status: response.status,
statusText: response.statusText,
data: data
}
} catch (error) {
logger.error(error);
return {
status: error.response.status,
statusText: error
}
}
try {
let response = await axios.request(options);
let data;

console.log(response.config);

if (response.config.responseType === 'arraybuffer') {

if (response.config.headers['Content-Type'] === 'application/json;charset=UTF-8') {
// We need to handle special charactesr such as åäöÅÄÖ
data = JSON.parse(response.data.toString('latin1'));
} else if (response.config.headers['Content-Type'] === 'application/zip') {
data = response.data;
}

} else if (response.config.responseType === 'blob') {
// console.log(response.headers);
// console.log(response.config);
// console.log(response.request);
data = response.data;
} else {
data = response.data;
}

// IPC does not allow you to directly return promises,
// you can only return basic types and objects that can be serialized.
return {
status: response.status,
statusText: response.statusText,
headers: JSON.stringify(response.headers),
config: JSON.stringify(response.config),
data: data
}

} catch (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// logger.error(error.response.status);
// logger.error(error.response.headers);
// logger.error(error.response.data);
logger.error(error.response);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
logger.error(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}

return error;
}
})
Loading

0 comments on commit c669df0

Please sign in to comment.