From a2d82b526e731123b1004045e064fca6229c46f1 Mon Sep 17 00:00:00 2001
From: Ivelin Megdanov -:-
-
+
diff --git a/src/js/api.js b/src/js/api.js
index c55fc68..d31a934 100644
--- a/src/js/api.js
+++ b/src/js/api.js
@@ -8,14 +8,11 @@ class API {
get(_endpoint) {
App.Loader.init();
- return axios.get(this.apiAdapter.baseURL + _endpoint)
- .then( r => r.data )
- .then (r => {
- App.Message.clear();
- App.Loader.destroy();
-
- return r;
- });
+ return this.apiAdapter.getBitcoinRatesForPeriod(_endpoint).then((r) => {
+ App.Message.clear();
+ App.Loader.destroy();
+ return r;
+ });
}
mapData(_r, _period) {
@@ -49,7 +46,7 @@ class API {
getBitcoinRatesNow() {
return this.apiAdapter.getBitcoinRatesNow();
}
-};
+}
// window.App.API = new API(App.apiFakeAdapter);
//window.App.API = new API(App.apiGoranAdapter);
diff --git a/src/js/apiCecoAdapter.js b/src/js/apiCecoAdapter.js
index 2b7534b..1135b48 100644
--- a/src/js/apiCecoAdapter.js
+++ b/src/js/apiCecoAdapter.js
@@ -1,12 +1,6 @@
window.App = window.App || {};
window.App.apiCecoAdapter = {
- baseURL: 'https://api.crypto-tab.com/v1/',
-
- get: function (_endpoint) {
- return App.API.get(_endpoint);
- },
-
mapData: function (response, dateLabelFormat) {
return response
.map((_rec) => ({
@@ -19,44 +13,43 @@ window.App.apiCecoAdapter = {
.reverse();
},
- _createDateAsUTC: function (date) {
- return new Date(
- Date.UTC(
- date.getFullYear(),
- date.getMonth(),
- date.getDate(),
- date.getHours(),
- date.getMinutes(),
- date.getSeconds()
- )
- );
+ getBitcoinRatesForPeriod: function (period) {
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({ type: 'getBitcoinPrice', period: period }, (response) => {
+ if (response && !response.error) {
+ resolve(response.data);
+ } else {
+ reject(response.error || 'Failed to retrieve Bitcoin price data');
+ }
+ });
+ });
},
getBitcoinRatesForAll: function () {
- return this.get('bitcoin/all');
+ return this.getBitcoinRatesForPeriod('ALL');
},
getBitcoinRatesForOneYear: function () {
- return this.get('bitcoin/year');
+ return this.getBitcoinRatesForPeriod('ONE_YEAR');
},
getBitcoinRatesForOneMonth: function () {
- return this.get('bitcoin/month');
+ return this.getBitcoinRatesForPeriod('ONE_MONTH');
},
getBitcoinRatesForOneWeek: function () {
- return this.get('bitcoin/week');
+ return this.getBitcoinRatesForPeriod('ONE_WEEK');
},
getBitcoinRatesForOneDay: function () {
- return this.get('bitcoin/day');
+ return this.getBitcoinRatesForPeriod('ONE_DAY');
},
getBitcoinRatesForOneHour: function () {
- return this.get('bitcoin/hour');
+ return this.getBitcoinRatesForPeriod('ONE_HOUR');
},
getBitcoinRatesNow: function () {
- return this.get('bitcoin/now');
+ return this.getBitcoinRatesForPeriod('NOW');
},
};
diff --git a/src/js/background.js b/src/js/background.js
new file mode 100644
index 0000000..92f9518
--- /dev/null
+++ b/src/js/background.js
@@ -0,0 +1,31 @@
+let bitcoinPriceData = {};
+
+async function fetchBitcoinPrice(period) {
+ const endpoints = {
+ ALL: 'bitcoin/all',
+ ONE_YEAR: 'bitcoin/year',
+ ONE_MONTH: 'bitcoin/month',
+ ONE_WEEK: 'bitcoin/week',
+ ONE_DAY: 'bitcoin/day',
+ ONE_HOUR: 'bitcoin/hour',
+ NOW: 'bitcoin/now',
+ };
+
+ try {
+ const response = await fetch(`https://api.crypto-tab.com/v1/${endpoints[period]}`);
+ const data = await response.json();
+ bitcoinPriceData[period] = data;
+ } catch (error) {
+ console.error(`Error fetching Bitcoin price for ${period}:`, error);
+ }
+}
+
+chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
+ if (request.type === 'getBitcoinPrice') {
+ const period = request.period;
+ fetchBitcoinPrice(period).then(() => {
+ sendResponse({ data: bitcoinPriceData[period], cached: false });
+ });
+ return true; // Indicates that the response will be sent asynchronously
+ }
+});
diff --git a/src/js/bitcoin.js b/src/js/bitcoin.js
index de9801d..dcb6b64 100644
--- a/src/js/bitcoin.js
+++ b/src/js/bitcoin.js
@@ -30,8 +30,7 @@ window.App.Bitcoin = {
this.classList.add('active');
const period = this.dataset.period;
- self.repositories[period]
- .getData()
+ self.getBitcoinData(period)
.then((_data) => self.chart.init(_data))
.catch((error) => {
self.handleChartRejection(period, error);
@@ -45,26 +44,21 @@ window.App.Bitcoin = {
App.Settings.get().then(({ period }) => {
const selectedTab = period ? Object.keys(this.PERIODS).indexOf(period) : 1;
-
self.$dataPeriods[selectedTab].click();
});
},
getBitcoinData(period) {
- switch (period) {
- case 'ALL':
- return App.API.getBitcoinRatesForAll();
- case 'ONE_YEAR':
- return App.API.getBitcoinRatesForOneYear();
- case 'ONE_MONTH':
- return App.API.getBitcoinRatesForOneMonth();
- case 'ONE_WEEK':
- return App.API.getBitcoinRatesForOneWeek();
- case 'ONE_DAY':
- return App.API.getBitcoinRatesForOneDay();
- case 'ONE_HOUR':
- return App.API.getBitcoinRatesForOneHour();
- }
+ return new Promise((resolve, reject) => {
+ this.repositories[period]
+ .getData()
+ .then((response) => {
+ resolve(response);
+ })
+ .catch((error) => {
+ reject(error || 'Failed to retrieve Bitcoin price data');
+ });
+ });
},
getLabelFormat(period) {
@@ -74,7 +68,7 @@ window.App.Bitcoin = {
case 'ONE_YEAR':
return 'MMM YYYY';
case 'ONE_MONTH':
- return 'Do MMM';
+ return 'D MMM';
case 'ONE_WEEK':
return 'dddd';
case 'ONE_DAY':
@@ -95,7 +89,6 @@ window.App.Bitcoin = {
this.chart.destroy();
} else if (_res.localData.length) {
App.Message.clear();
-
this.chart.init(_res.localData);
this.setLastUpdated(true);
}
@@ -103,7 +96,6 @@ window.App.Bitcoin = {
},
handleNowRejection() {
this.isLocalNowDataOld = true;
-
App.Loader.destroy();
},
@@ -112,29 +104,28 @@ window.App.Bitcoin = {
const storageSetting =
App.ENV.platform === 'EXTENSION' ? 'BROWSER_STORAGE' : 'LOCAL_STORAGE';
- Object.keys(this.PERIODS).forEach(
- (period) =>
- (this.repositories[period] = new SuperRepo({
- storage: storageSetting,
- name: 'bitcoin-' + period,
- outOfDateAfter: 15 * 60 * 1000,
- mapData: (r) => App.API.mapData(r, this.getLabelFormat(period)),
- request: () =>
- this.getBitcoinData(period)
- .then((res) => {
- this.isLocalChartDataOld = false;
- return res;
- })
- .catch((jqXHR, textStatus, errorThrown) => {
- this.handleChartRejection(period, jqXHR);
- }),
- }))
- );
+ Object.keys(this.PERIODS).forEach((period) => {
+ this.repositories[period] = new SuperRepo({
+ storage: storageSetting,
+ name: 'bitcoin-' + period,
+ outOfDateAfter: 0.1 * 60 * 1000, // 15 minutes
+ mapData: (r) => App.API.mapData(r, this.getLabelFormat(period)),
+ request: () =>
+ this.getBitcoinDataFromBackground(period)
+ .then((res) => {
+ this.isLocalChartDataOld = false;
+ return res;
+ })
+ .catch((jqXHR, textStatus, errorThrown) => {
+ this.handleChartRejection(period, jqXHR);
+ }),
+ });
+ });
this.repositories['NOW'] = new SuperRepo({
storage: storageSetting,
name: 'bitcoin-NOW',
- outOfDateAfter: 3 * 60 * 1000,
+ outOfDateAfter: 0.1 * 60 * 1000, // 3 minutes
mapData: (data) => {
const { value, changePercent } = data[0];
const { dayAgo, weekAgo, monthAgo } = changePercent;
@@ -145,7 +136,7 @@ window.App.Bitcoin = {
};
},
request: () =>
- App.API.getBitcoinRatesNow()
+ this.getBitcoinDataFromBackground('NOW')
.then((res) => {
this.isLocalNowDataOld = false;
return res;
@@ -156,6 +147,18 @@ window.App.Bitcoin = {
});
},
+ getBitcoinDataFromBackground(period) {
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({ type: 'getBitcoinPrice', period: period }, (response) => {
+ if (response && !response.error) {
+ resolve(response.data);
+ } else {
+ reject(response.error || 'Failed to retrieve Bitcoin price data');
+ }
+ });
+ });
+ },
+
$priceNow: document.querySelector('#price-now'),
setPriceNow(_price) {
this.$priceNow.textContent = App.Utils.formatPrice(Math.round(_price));
@@ -201,16 +204,16 @@ window.App.Bitcoin = {
}
const getSignedPercentage = (_number) => {
- const isChangePisitive = _number >= 0;
+ const isChangePositive = _number >= 0;
const isChangeZero = _number === 0;
- return isChangePisitive && !isChangeZero ? `+${_number}%` : `${_number}%`;
+ return isChangePositive && !isChangeZero ? `+${_number}%` : `${_number}%`;
};
const getVisualClass = (_number) => {
- const isChangePisitive = _number >= 0;
+ const isChangePositive = _number >= 0;
const isChangeZero = _number === 0;
- return isChangeZero ? '' : isChangePisitive ? 'positive' : 'negative';
+ return isChangeZero ? '' : isChangePositive ? 'positive' : 'negative';
};
this.$change.innerHTML = ` (${getSignedPercentage(changePercent)}
diff --git a/src/manifest.json b/src/manifest.json
index 83b481a..871a611 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -15,5 +15,8 @@
"host_permissions": [
"https://bitcoin-price-api.devlabs-projects.info/**/*",
"https://api.crypto-tab.com/**/*"
- ]
+ ],
+ "background": {
+ "service_worker": "js/background.js"
+ }
}
From 9fcda0355f2f74fbe31bfb79925662980abc83ec Mon Sep 17 00:00:00 2001
From: Ivelin Megdanov -:-
- Bitcoin price is
+ Crypto price is
...
@@ -145,6 +150,7 @@
+
diff --git a/src/js/api.js b/src/js/api.js
index d31a934..aa03b9f 100644
--- a/src/js/api.js
+++ b/src/js/api.js
@@ -50,4 +50,4 @@ class API {
// window.App.API = new API(App.apiFakeAdapter);
//window.App.API = new API(App.apiGoranAdapter);
-window.App.API = new API(App.apiCecoAdapter);
+window.App.API = new API(App.apiBoyoAdapter);
diff --git a/src/js/apiBoyoAdapter.js b/src/js/apiBoyoAdapter.js
new file mode 100644
index 0000000..b88af06
--- /dev/null
+++ b/src/js/apiBoyoAdapter.js
@@ -0,0 +1,28 @@
+window.App.apiBoyoAdapter = {
+ mapData: function (response, dateLabelFormat) {
+ return response
+ .map((_rec) => ({
+ value: _rec.value,
+ timestamp: dayjs
+ .utc(_rec.timestamp * 1000)
+ .local()
+ .format(dateLabelFormat),
+ }))
+ .reverse();
+ },
+
+ getCryptoRatesForPeriod: function (period, cryptoType) {
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage(
+ { type: 'getCryptoPrice', period: period, cryptoType: cryptoType },
+ (response) => {
+ if (response && !response.error) {
+ resolve(response.data);
+ } else {
+ reject(response.error || `Failed to retrieve ${cryptoType} price data`);
+ }
+ }
+ );
+ });
+ },
+};
diff --git a/src/js/background.js b/src/js/background.js
index 92f9518..639873b 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -1,30 +1,33 @@
-let bitcoinPriceData = {};
+let cryptoPriceData = {
+ bitcoin: {},
+ ethereum: {},
+};
-async function fetchBitcoinPrice(period) {
+async function fetchCryptoPrice(period, cryptoType) {
const endpoints = {
- ALL: 'bitcoin/all',
- ONE_YEAR: 'bitcoin/year',
- ONE_MONTH: 'bitcoin/month',
- ONE_WEEK: 'bitcoin/week',
- ONE_DAY: 'bitcoin/day',
- ONE_HOUR: 'bitcoin/hour',
- NOW: 'bitcoin/now',
+ ALL: `${cryptoType}/all`,
+ ONE_YEAR: `${cryptoType}/year`,
+ ONE_MONTH: `${cryptoType}/month`,
+ ONE_WEEK: `${cryptoType}/week`,
+ ONE_DAY: `${cryptoType}/day`,
+ ONE_HOUR: `${cryptoType}/hour`,
+ NOW: `${cryptoType}/now`,
};
try {
- const response = await fetch(`https://api.crypto-tab.com/v1/${endpoints[period]}`);
+ const response = await fetch(`http://localhost:3000/v1/${endpoints[period]}`); // TODO: Update to use the deployed API (maybe use a config file to store the base URL)
const data = await response.json();
- bitcoinPriceData[period] = data;
+ cryptoPriceData[cryptoType][period] = data;
} catch (error) {
- console.error(`Error fetching Bitcoin price for ${period}:`, error);
+ console.error(`Error fetching ${cryptoType} price for ${period}:`, error);
}
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
- if (request.type === 'getBitcoinPrice') {
- const period = request.period;
- fetchBitcoinPrice(period).then(() => {
- sendResponse({ data: bitcoinPriceData[period], cached: false });
+ if (request.type === 'getCryptoPrice') {
+ const { period, cryptoType } = request;
+ fetchCryptoPrice(period, cryptoType).then(() => {
+ sendResponse({ data: cryptoPriceData[cryptoType][period], cached: false });
});
return true; // Indicates that the response will be sent asynchronously
}
diff --git a/src/js/bitcoin.js b/src/js/bitcoin.js
index 98200c0..f01fe74 100644
--- a/src/js/bitcoin.js
+++ b/src/js/bitcoin.js
@@ -21,6 +21,10 @@ window.App.Bitcoin = {
chart: null,
$dataPeriods: document.querySelectorAll('.js-period'),
+ $cryptoToggle: document.querySelectorAll('input[name="crypto"]'),
+ $cryptoTypeLabel: document.getElementById('crypto-type'),
+ currentCrypto: '',
+
initEvents() {
const self = this;
@@ -30,33 +34,64 @@ window.App.Bitcoin = {
this.classList.add('active');
const period = this.dataset.period;
- self.getBitcoinData(period)
+ self.getCryptoData(period, self.currentCrypto)
.then((_data) => self.chart.init(_data))
.catch((error) => {
- self.handleChartRejection(period, error);
+ self.handleChartRejection(period, self.currentCrypto, error);
});
App.Settings.set('period', this.dataset.period);
- self.setPriceChange();
+ self.setPriceChange(self.currentCrypto);
+ });
+ });
+
+ [...self.$cryptoToggle].forEach((el) => {
+ el.addEventListener('change', function () {
+ if (this.checked) {
+ self.currentCrypto = this.value;
+ self.updateCryptoTypeLabel(self.currentCrypto);
+ const period = document.querySelector('.js-period.active').dataset.period;
+ self.getCryptoData(period, self.currentCrypto)
+ .then((_data) => self.chart.init(_data))
+ .catch((error) => {
+ self.handleChartRejection(period, self.currentCrypto, error);
+ });
+
+ App.Settings.set('cryptoType', self.currentCrypto);
+
+ self.setPriceChange(self.currentCrypto);
+ self.setLastUpdated();
+ self.displayPriceNow();
+ }
});
});
- App.Settings.get().then(({ period }) => {
+ App.Settings.get().then(({ period, cryptoType }) => {
+ self.currentCrypto =
+ cryptoType || document.querySelector('input[name="crypto"]:checked').value;
const selectedTab = period ? Object.keys(this.PERIODS).indexOf(period) : 1;
+ document.querySelector(`input[name="crypto"][value="${self.currentCrypto}"]`).checked =
+ true;
+ self.initRepositories();
self.$dataPeriods[selectedTab].click();
});
},
- getBitcoinData(period) {
+ updateCryptoTypeLabel(cryptoType) {
+ const label = cryptoType.charAt(0).toUpperCase() + cryptoType.slice(1);
+ this.$cryptoTypeLabel.textContent = label;
+ },
+
+ getCryptoData(period, cryptoType) {
return new Promise((resolve, reject) => {
- this.repositories[period]
+ this.repositories[cryptoType][period]
.getData()
.then((response) => {
resolve(response);
})
.catch((error) => {
- reject(error || 'Failed to retrieve Bitcoin price data');
+ reject(error || `Failed to retrieve ${cryptoType} price data`);
});
});
},
@@ -78,19 +113,19 @@ window.App.Bitcoin = {
}
},
- handleChartRejection(_period, _error) {
+ handleChartRejection(_period, _cryptoType, _error) {
this.isLocalChartDataOld = true;
- this.repositories[_period].getDataUpToDateStatus().then((_res) => {
+ this.repositories[_cryptoType][_period].getDataUpToDateStatus().then((_res) => {
App.Loader.destroy();
if (_res.localData === null) {
- App.Message.fireError("That's extremely sad. " + _error);
+ App.Message.fireError(`That's extremely sad. ${_error}`);
this.chart.destroy();
} else if (_res.localData.length) {
App.Message.clear();
this.chart.init(_res.localData);
- this.setLastUpdated(true);
+ this.setLastUpdated();
}
});
},
@@ -104,58 +139,64 @@ window.App.Bitcoin = {
const storageSetting =
App.ENV.platform === 'EXTENSION' ? 'BROWSER_STORAGE' : 'LOCAL_STORAGE';
- Object.keys(this.PERIODS).forEach((period) => {
- this.repositories[period] = new SuperRepo({
+ ['bitcoin', 'ethereum'].forEach((cryptoType) => {
+ this.repositories[cryptoType] = {};
+ Object.keys(this.PERIODS).forEach((period) => {
+ this.repositories[cryptoType][period] = new SuperRepo({
+ storage: storageSetting,
+ name: `${cryptoType}-${period}`,
+ outOfDateAfter: 15 * 60 * 1000, // 15 minutes
+ mapData: (r) => App.API.mapData(r, this.getLabelFormat(period)),
+ request: () =>
+ this.getCryptoDataFromBackground(period, cryptoType)
+ .then((res) => {
+ this.isLocalChartDataOld = false;
+ return res;
+ })
+ .catch((jqXHR, textStatus, errorThrown) => {
+ this.handleChartRejection(period, cryptoType, jqXHR);
+ }),
+ });
+ });
+
+ this.repositories[cryptoType]['NOW'] = new SuperRepo({
storage: storageSetting,
- name: 'bitcoin-' + period,
- outOfDateAfter: 15 * 60 * 1000, // 15 minutes
- mapData: (r) => App.API.mapData(r, this.getLabelFormat(period)),
+ name: `${cryptoType}-NOW`,
+ outOfDateAfter: 3 * 60 * 1000, // 3 minutes
+ mapData: (data) => {
+ const { value, changePercent } = data[0];
+ const { dayAgo, weekAgo, monthAgo } = changePercent;
+
+ return {
+ price: value,
+ changePercent: { dayAgo, weekAgo, monthAgo },
+ };
+ },
request: () =>
- this.getBitcoinDataFromBackground(period)
+ this.getCryptoDataFromBackground('NOW', cryptoType)
.then((res) => {
- this.isLocalChartDataOld = false;
+ this.isLocalNowDataOld = false;
return res;
})
- .catch((jqXHR, textStatus, errorThrown) => {
- this.handleChartRejection(period, jqXHR);
+ .catch(() => {
+ this.handleNowRejection();
}),
});
});
-
- this.repositories['NOW'] = new SuperRepo({
- storage: storageSetting,
- name: 'bitcoin-NOW',
- outOfDateAfter: 3 * 60 * 1000, // 3 minutes
- mapData: (data) => {
- const { value, changePercent } = data[0];
- const { dayAgo, weekAgo, monthAgo } = changePercent;
-
- return {
- price: value,
- changePercent: { dayAgo, weekAgo, monthAgo },
- };
- },
- request: () =>
- this.getBitcoinDataFromBackground('NOW')
- .then((res) => {
- this.isLocalNowDataOld = false;
- return res;
- })
- .catch(() => {
- this.handleNowRejection();
- }),
- });
},
- getBitcoinDataFromBackground(period) {
+ getCryptoDataFromBackground(period, cryptoType) {
return new Promise((resolve, reject) => {
- chrome.runtime.sendMessage({ type: 'getBitcoinPrice', period: period }, (response) => {
- if (response && !response.error) {
- resolve(response.data);
- } else {
- reject(response.error || 'Failed to retrieve Bitcoin price data');
+ chrome.runtime.sendMessage(
+ { type: 'getCryptoPrice', period: period, cryptoType: cryptoType },
+ (response) => {
+ if (response && !response.error) {
+ resolve(response.data);
+ } else {
+ reject(response.error || `Failed to retrieve ${cryptoType} price data`);
+ }
}
- });
+ );
});
},
@@ -165,8 +206,8 @@ window.App.Bitcoin = {
},
$change: document.querySelector('#change'),
- async setPriceChange() {
- let { localData } = await this.repositories['NOW'].getDataUpToDateStatus();
+ async setPriceChange(cryptoType) {
+ let { localData } = await this.repositories[cryptoType]['NOW'].getDataUpToDateStatus();
if (!localData) {
return;
}
@@ -222,12 +263,13 @@ window.App.Bitcoin = {
$lastUpdated: document.querySelector('#last-updated'),
setLastUpdated() {
- this.repositories['NOW'].getDataUpToDateStatus().then((info) => {
+ const cryptoType = this.currentCrypto;
+ this.repositories[cryptoType]['NOW'].getDataUpToDateStatus().then((info) => {
const prettyLastUpdatedTime = dayjs(info.lastFetched).fromNow();
this.$lastUpdated.innerHTML =
this.isLocalChartDataOld || this.isLocalNowDataOld
? `${prettyLastUpdatedTime}.
- Data request failed. Refresh the page to try again.`
+ Data request failed. Refresh the page to try again.`
: `${prettyLastUpdatedTime}.`;
this.$lastUpdated.setAttribute('data-tooltip', dayjs(info.lastFetched).calendar());
@@ -235,15 +277,16 @@ window.App.Bitcoin = {
},
displayPriceNow() {
- this.repositories['NOW']
+ const cryptoType = this.currentCrypto;
+ this.repositories[cryptoType]['NOW']
.getData()
.then((_data) => {
- this.setPriceChange();
+ this.setPriceChange(cryptoType);
this.setLastUpdated();
})
.catch(() => {
this.handleNowRejection();
- this.setPriceChange();
+ this.setPriceChange(cryptoType);
this.setLastUpdated();
});
@@ -255,8 +298,13 @@ window.App.Bitcoin = {
this.chart = new App.Chart(this.$chart);
this.initRepositories();
- this.displayPriceNow();
-
this.initEvents();
+
+ App.Settings.get().then(({ cryptoType }) => {
+ this.currentCrypto =
+ cryptoType || document.querySelector('input[name="crypto"]:checked').value;
+ this.updateCryptoTypeLabel(this.currentCrypto);
+ this.displayPriceNow();
+ });
},
};
From 1101712f37909818e6beddc0a7cecf117dd01707 Mon Sep 17 00:00:00 2001
From: Ivelin Megdanov
Open Source
|
API
+