Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
b0fb986208 | |||
0b59fc639d | |||
7ddd6f27b5 | |||
c5d56f4b47 | |||
2f2b712999 | |||
c2fd31f5e5 | |||
f2d70f9070 | |||
f41dd9cd8e | |||
7d238b4935 | |||
da6591fca0 | |||
1f9b9e9998 | |||
49c4ea306d | |||
ccb5c664ef | |||
97e165ff69 | |||
45aefb6a45 | |||
2435535975 | |||
bd3d43bf05 | |||
02dc7c52b1 | |||
ff59fd4196 |
@ -9,6 +9,7 @@
|
||||
],
|
||||
"attributeSort": "ASC",
|
||||
"endOfLine": "auto",
|
||||
"plugins": ["prettier-plugin-organize-attributes"],
|
||||
"printWidth": 80,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
|
34
CHANGELOG.md
34
CHANGELOG.md
@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 1.304.0 - 2023-08-27
|
||||
|
||||
### Added
|
||||
|
||||
- Added health check endpoints for data enhancers
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgraded `Nx` from version `16.7.2` to `16.7.4`
|
||||
- Upgraded `prettier` from version `2.8.4` to `3.0.2`
|
||||
|
||||
## 1.303.0 - 2023-08-23
|
||||
|
||||
### Added
|
||||
|
||||
- Added a blog post: _Ghostfolio joins OSS Friends_
|
||||
|
||||
### Changed
|
||||
|
||||
- Refreshed the cryptocurrencies list
|
||||
- Improved the _OSS Friends_ page
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue with the _Trackinsight_ data enhancer for asset profile data
|
||||
|
||||
## 1.302.0 - 2023-08-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved the language localization for German (`de`)
|
||||
- Upgraded `angular` from version `16.1.8` to `16.2.1`
|
||||
- Upgraded `Nx` from version `16.6.0` to `16.7.2`
|
||||
|
||||
## 1.301.1 - 2023-08-19
|
||||
|
||||
### Added
|
||||
|
@ -41,9 +41,8 @@ export class AuthController {
|
||||
@Param('accessToken') accessToken: string
|
||||
): Promise<OAuthResponse> {
|
||||
try {
|
||||
const authToken = await this.authService.validateAnonymousLogin(
|
||||
accessToken
|
||||
);
|
||||
const authToken =
|
||||
await this.authService.validateAnonymousLogin(accessToken);
|
||||
return { authToken };
|
||||
} catch {
|
||||
throw new HttpException(
|
||||
|
@ -7,10 +7,10 @@ import {
|
||||
UseGuards
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { parseISO } from 'date-fns';
|
||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||
|
||||
import { ExchangeRateService } from './exchange-rate.service';
|
||||
import { parseISO } from 'date-fns';
|
||||
|
||||
@Controller('exchange-rate')
|
||||
export class ExchangeRateController {
|
||||
|
@ -18,6 +18,19 @@ export class HealthController {
|
||||
@Get()
|
||||
public async getHealth() {}
|
||||
|
||||
@Get('data-enhancer/:name')
|
||||
public async getHealthOfDataEnhancer(@Param('name') name: string) {
|
||||
const hasResponse =
|
||||
await this.healthService.hasResponseFromDataEnhancer(name);
|
||||
|
||||
if (hasResponse !== true) {
|
||||
throw new HttpException(
|
||||
getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE),
|
||||
StatusCodes.SERVICE_UNAVAILABLE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Get('data-provider/:dataSource')
|
||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||
public async getHealthOfDataProvider(
|
||||
@ -30,9 +43,8 @@ export class HealthController {
|
||||
);
|
||||
}
|
||||
|
||||
const hasResponse = await this.healthService.hasResponseFromDataProvider(
|
||||
dataSource
|
||||
);
|
||||
const hasResponse =
|
||||
await this.healthService.hasResponseFromDataProvider(dataSource);
|
||||
|
||||
if (hasResponse !== true) {
|
||||
throw new HttpException(
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
|
||||
import { DataEnhancerModule } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.module';
|
||||
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
@ -7,7 +8,7 @@ import { HealthService } from './health.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HealthController],
|
||||
imports: [ConfigurationModule, DataProviderModule],
|
||||
imports: [ConfigurationModule, DataEnhancerModule, DataProviderModule],
|
||||
providers: [HealthService]
|
||||
})
|
||||
export class HealthModule {}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/data-enhancer.service';
|
||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource } from '@prisma/client';
|
||||
@ -5,9 +6,14 @@ import { DataSource } from '@prisma/client';
|
||||
@Injectable()
|
||||
export class HealthService {
|
||||
public constructor(
|
||||
private readonly dataEnhancerService: DataEnhancerService,
|
||||
private readonly dataProviderService: DataProviderService
|
||||
) {}
|
||||
|
||||
public async hasResponseFromDataEnhancer(aName: string) {
|
||||
return this.dataEnhancerService.enhance(aName);
|
||||
}
|
||||
|
||||
public async hasResponseFromDataProvider(aDataSource: DataSource) {
|
||||
return this.dataProviderService.checkQuote(aDataSource);
|
||||
}
|
||||
|
@ -470,9 +470,8 @@ export class PortfolioService {
|
||||
transactionPoints[0]?.date ?? format(new Date(), DATE_FORMAT)
|
||||
);
|
||||
const startDate = this.getStartDate(dateRange, portfolioStart);
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
startDate
|
||||
);
|
||||
const currentPositions =
|
||||
await portfolioCalculator.getCurrentPositions(startDate);
|
||||
|
||||
const cashDetails = await this.accountService.getCashDetails({
|
||||
filters,
|
||||
@ -810,9 +809,8 @@ export class PortfolioService {
|
||||
const transactionPoints = portfolioCalculator.getTransactionPoints();
|
||||
|
||||
const portfolioStart = parseDate(transactionPoints[0].date);
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
portfolioStart
|
||||
);
|
||||
const currentPositions =
|
||||
await portfolioCalculator.getCurrentPositions(portfolioStart);
|
||||
|
||||
const position = currentPositions.positions.find(
|
||||
(item) => item.symbol === aSymbol
|
||||
@ -1046,9 +1044,8 @@ export class PortfolioService {
|
||||
|
||||
const portfolioStart = parseDate(transactionPoints[0].date);
|
||||
const startDate = this.getStartDate(dateRange, portfolioStart);
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
startDate
|
||||
);
|
||||
const currentPositions =
|
||||
await portfolioCalculator.getCurrentPositions(startDate);
|
||||
|
||||
const positions = currentPositions.positions.filter(
|
||||
(item) => !item.quantity.eq(0)
|
||||
@ -1238,9 +1235,8 @@ export class PortfolioService {
|
||||
portfolioCalculator.setTransactionPoints(transactionPoints);
|
||||
|
||||
const portfolioStart = parseDate(transactionPoints[0].date);
|
||||
const currentPositions = await portfolioCalculator.getCurrentPositions(
|
||||
portfolioStart
|
||||
);
|
||||
const currentPositions =
|
||||
await portfolioCalculator.getCurrentPositions(portfolioStart);
|
||||
|
||||
const positions = currentPositions.positions.filter(
|
||||
(item) => !item.quantity.eq(0)
|
||||
|
@ -93,9 +93,8 @@ export class SubscriptionService {
|
||||
|
||||
public async createSubscriptionViaStripe(aCheckoutSessionId: string) {
|
||||
try {
|
||||
const session = await this.stripe.checkout.sessions.retrieve(
|
||||
aCheckoutSessionId
|
||||
);
|
||||
const session =
|
||||
await this.stripe.checkout.sessions.retrieve(aCheckoutSessionId);
|
||||
|
||||
await this.createSubscription({
|
||||
price: session.amount_total / 100,
|
||||
|
@ -15,13 +15,13 @@ import {
|
||||
import { REQUEST } from '@nestjs/core';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { DataSource } from '@prisma/client';
|
||||
import { parseISO } from 'date-fns';
|
||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||
import { isDate, isEmpty } from 'lodash';
|
||||
|
||||
import { LookupItem } from './interfaces/lookup-item.interface';
|
||||
import { SymbolItem } from './interfaces/symbol-item.interface';
|
||||
import { SymbolService } from './symbol.service';
|
||||
import { parseISO } from 'date-fns';
|
||||
|
||||
@Controller('symbol')
|
||||
export class SymbolController {
|
||||
|
@ -51,7 +51,9 @@
|
||||
"3FT": "ThreeFold Token",
|
||||
"3ULL": "3ULL Coin",
|
||||
"3XD": "3DChain",
|
||||
"420CHAN": "420chan",
|
||||
"4ART": "4ART Coin",
|
||||
"4CHAN": "4Chan",
|
||||
"4JNET": "4JNET",
|
||||
"77G": "GraphenTech",
|
||||
"7E": "7ELEVEN",
|
||||
@ -60,6 +62,7 @@
|
||||
"8BT": "8 Circuit Studios",
|
||||
"8PAY": "8Pay",
|
||||
"8X8": "8X8 Protocol",
|
||||
"9GAG": "9GAG",
|
||||
"A5T": "Alpha5",
|
||||
"AAA": "Moon Rabbit",
|
||||
"AAB": "AAX Token",
|
||||
@ -101,6 +104,7 @@
|
||||
"ACN": "AvonCoin",
|
||||
"ACOIN": "ACoin",
|
||||
"ACP": "Anarchists Prime",
|
||||
"ACQ": "Acquire.Fi",
|
||||
"ACS": "Access Protocol",
|
||||
"ACT": "Achain",
|
||||
"ACTIN": "Actinium",
|
||||
@ -180,7 +184,7 @@
|
||||
"AGX": "Agricoin",
|
||||
"AHOO": "Ahoolee",
|
||||
"AHT": "AhaToken",
|
||||
"AI": "Multiverse",
|
||||
"AI": "AiDoge",
|
||||
"AIB": "AdvancedInternetBlock",
|
||||
"AIBB": "AiBB",
|
||||
"AIBK": "AIB Utility Token",
|
||||
@ -213,6 +217,7 @@
|
||||
"AKA": "Akroma",
|
||||
"AKITA": "Akita Inu",
|
||||
"AKN": "Akoin",
|
||||
"AKNC": "Aave KNC v1",
|
||||
"AKRO": "Akropolis",
|
||||
"AKT": "Akash Network",
|
||||
"AKTIO": "AKTIO Coin",
|
||||
@ -237,12 +242,14 @@
|
||||
"ALIC": "AliCoin",
|
||||
"ALICE": "My Neighbor Alice",
|
||||
"ALIEN": "AlienCoin",
|
||||
"ALINK": "Aave LINK v1",
|
||||
"ALIS": "ALISmedia",
|
||||
"ALITA": "Alita Network",
|
||||
"ALIX": "AlinX",
|
||||
"ALKI": "Alkimi",
|
||||
"ALLBI": "ALL BEST ICO",
|
||||
"ALLEY": "NFT Alley",
|
||||
"ALLIN": "All in",
|
||||
"ALN": "Aluna",
|
||||
"ALOHA": "Aloha",
|
||||
"ALP": "Alphacon",
|
||||
@ -410,12 +417,14 @@
|
||||
"ARIX": "Arix",
|
||||
"ARK": "ARK",
|
||||
"ARKER": "Arker",
|
||||
"ARKM": "Arkham",
|
||||
"ARKN": "Ark Rivals",
|
||||
"ARM": "Armory Coin",
|
||||
"ARMOR": "ARMOR",
|
||||
"ARMR": "ARMR",
|
||||
"ARMS": "2Acoin",
|
||||
"ARNA": "ARNA Panacea",
|
||||
"ARNM": "Arenum",
|
||||
"ARNO": "ARNO",
|
||||
"ARNX": "Aeron",
|
||||
"ARNXM": "Armor NXM",
|
||||
@ -472,6 +481,7 @@
|
||||
"ASTO": "Altered State Token",
|
||||
"ASTON": "Aston",
|
||||
"ASTR": "Astar",
|
||||
"ASTRAFER": "Astrafer",
|
||||
"ASTRAL": "Astral",
|
||||
"ASTRO": "AstroSwap",
|
||||
"ASTROC": "Astroport Classic",
|
||||
@ -531,6 +541,7 @@
|
||||
"AURY": "Aurory",
|
||||
"AUSCM": "Auric Network",
|
||||
"AUSD": "Appeal dollar",
|
||||
"AUSDC": "Aave USDC v1",
|
||||
"AUT": "Autoria",
|
||||
"AUTHORSHIP": "Authorship",
|
||||
"AUTO": "Auto",
|
||||
@ -612,6 +623,7 @@
|
||||
"BACK": "DollarBack",
|
||||
"BACOIN": "BACoin",
|
||||
"BACON": "BaconDAO (BACON)",
|
||||
"BAD": "Bad Idea AI",
|
||||
"BADGER": "Badger DAO",
|
||||
"BAG": "BondAppetit",
|
||||
"BAGS": "Basis Gold Share",
|
||||
@ -662,6 +674,7 @@
|
||||
"BBCT": "TraDove B2BCoin",
|
||||
"BBDT": "BBD Token",
|
||||
"BBF": "Bubblefong",
|
||||
"BBFT": "Block Busters Tech Token",
|
||||
"BBG": "BigBang",
|
||||
"BBGC": "BigBang Game",
|
||||
"BBI": "BelugaPay",
|
||||
@ -725,6 +738,7 @@
|
||||
"BDX": "Beldex",
|
||||
"BDY": "Buddy DAO",
|
||||
"BEACH": "BeachCoin",
|
||||
"BEAI": "BeNFT Solutions",
|
||||
"BEAM": "Beam",
|
||||
"BEAN": "BeanCash",
|
||||
"BEAST": "CryptoBeast",
|
||||
@ -806,6 +820,7 @@
|
||||
"BIDR": "Binance IDR Stable Coin",
|
||||
"BIFI": "Beefy.Finance",
|
||||
"BIFIF": "BiFi",
|
||||
"BIG": "Big Eyes",
|
||||
"BIGHAN": "BighanCoin",
|
||||
"BIGSB": "BigShortBets",
|
||||
"BIGUP": "BigUp",
|
||||
@ -1090,6 +1105,7 @@
|
||||
"BRNK": "Brank",
|
||||
"BRNX": "Bronix",
|
||||
"BRO": "Bitradio",
|
||||
"BROCK": "Bitrock",
|
||||
"BRONZ": "BitBronze",
|
||||
"BRT": "Bikerush",
|
||||
"BRTR": "Barter",
|
||||
@ -1226,7 +1242,7 @@
|
||||
"BULL": "Bullieverse",
|
||||
"BULLC": "BuySell",
|
||||
"BULLION": "BullionFX",
|
||||
"BULLS": "BullshitCoin",
|
||||
"BULLS": "Bull Coin",
|
||||
"BULLSH": "Bullshit Inu",
|
||||
"BUMN": "BUMooN",
|
||||
"BUMP": "Bumper",
|
||||
@ -1319,8 +1335,10 @@
|
||||
"CAP": "BottleCaps",
|
||||
"CAPD": "Capdax",
|
||||
"CAPP": "Cappasity",
|
||||
"CAPRICOIN": "CapriCoin",
|
||||
"CAPS": "Ternoa",
|
||||
"CAPT": "Bitcoin Captain",
|
||||
"CAPTAINPLANET": "Captain Planet",
|
||||
"CAR": "CarBlock",
|
||||
"CARAT": "Carats Token",
|
||||
"CARBON": "Carboncoin",
|
||||
@ -1478,6 +1496,7 @@
|
||||
"CHECKR": "CheckerChain",
|
||||
"CHECOIN": "CheCoin",
|
||||
"CHEDDA": "Chedda",
|
||||
"CHEEL": "Cheelee",
|
||||
"CHEESE": "CHEESE",
|
||||
"CHEESUS": "Cheesus",
|
||||
"CHEQ": "CHEQD Network",
|
||||
@ -1520,7 +1539,8 @@
|
||||
"CHX": "Own",
|
||||
"CHY": "Concern Poverty Chain",
|
||||
"CHZ": "Chiliz",
|
||||
"CIC": "CIChain",
|
||||
"CIC": "Crazy Internet Coin",
|
||||
"CICHAIN": "CIChain",
|
||||
"CIF": "Crypto Improvement Fund",
|
||||
"CIM": "COINCOME",
|
||||
"CIN": "CinderCoin",
|
||||
@ -1630,7 +1650,6 @@
|
||||
"COB": "Cobinhood",
|
||||
"COC": "Coin of the champions",
|
||||
"COCK": "Shibacock",
|
||||
"COCOS": "COCOS BCX",
|
||||
"CODEO": "Codeo Token",
|
||||
"CODEX": "CODEX Finance",
|
||||
"CODI": "Codi Finance",
|
||||
@ -1659,7 +1678,7 @@
|
||||
"COLX": "ColossusCoinXT",
|
||||
"COM": "Coliseum",
|
||||
"COMB": "Combo",
|
||||
"COMBO": "Furucombo",
|
||||
"COMBO": "COMBO",
|
||||
"COMFI": "CompliFi",
|
||||
"COMM": "Community Coin",
|
||||
"COMMUNITYCOIN": "Community Coin",
|
||||
@ -1672,7 +1691,6 @@
|
||||
"CONI": "CoinBene",
|
||||
"CONS": "ConSpiracy Coin",
|
||||
"CONSENTIUM": "Consentium",
|
||||
"CONT": "Contentos",
|
||||
"CONUN": "CONUN",
|
||||
"CONV": "Convergence",
|
||||
"COOK": "Cook",
|
||||
@ -1683,17 +1701,19 @@
|
||||
"COPS": "Cops Finance",
|
||||
"COR": "Corion",
|
||||
"CORAL": "CoralPay",
|
||||
"CORE": "Coreum",
|
||||
"CORE": "Core",
|
||||
"COREDAO": "coreDAO",
|
||||
"COREG": "Core Group Asset",
|
||||
"COREUM": "Coreum",
|
||||
"CORGI": "Corgi Inu",
|
||||
"CORN": "CORN",
|
||||
"CORX": "CorionX",
|
||||
"COS": "COS",
|
||||
"COS": "Contentos",
|
||||
"COSHI": "CoShi Inu",
|
||||
"COSM": "CosmoChain",
|
||||
"COSMIC": "CosmicSwap",
|
||||
"COSP": "Cosplay Token",
|
||||
"COSS": "COS",
|
||||
"COSX": "Cosmecoin",
|
||||
"COT": "CoTrader",
|
||||
"COTI": "COTI",
|
||||
@ -1729,7 +1749,7 @@
|
||||
"CPOOL": "Clearpool",
|
||||
"CPROP": "CPROP",
|
||||
"CPRX": "Crypto Perx",
|
||||
"CPS": "CapriCoin",
|
||||
"CPS": "Cryptostone",
|
||||
"CPT": "Cryptaur",
|
||||
"CPU": "CPUcoin",
|
||||
"CPX": "Apex Token",
|
||||
@ -1796,6 +1816,7 @@
|
||||
"CRTS": "Cratos",
|
||||
"CRU": "Crust Network",
|
||||
"CRV": "Curve DAO Token",
|
||||
"CRVUSD": "crvUSD",
|
||||
"CRW": "Crown Coin",
|
||||
"CRWD": "CRWD Network",
|
||||
"CRWNY": "Crowny Token",
|
||||
@ -1843,7 +1864,7 @@
|
||||
"CTLX": "Cash Telex",
|
||||
"CTN": "Continuum Finance",
|
||||
"CTO": "Crypto",
|
||||
"CTP": "Captain Planet",
|
||||
"CTP": "Ctomorrow Platform",
|
||||
"CTPL": "Cultiplan",
|
||||
"CTPT": "Contents Protocol",
|
||||
"CTR": "Creator Platform",
|
||||
@ -2007,6 +2028,7 @@
|
||||
"DBC": "DeepBrain Chain",
|
||||
"DBCCOIN": "Datablockchain",
|
||||
"DBD": "Day By Day",
|
||||
"DBEAR": "DBear Coin",
|
||||
"DBET": "Decent.bet",
|
||||
"DBIC": "DubaiCoin",
|
||||
"DBIX": "DubaiCoin",
|
||||
@ -2058,6 +2080,7 @@
|
||||
"DEEP": "DeepCloud AI",
|
||||
"DEEPG": "Deep Gold",
|
||||
"DEEX": "DEEX",
|
||||
"DEEZ": "DEEZ NUTS",
|
||||
"DEFI": "Defi",
|
||||
"DEFI5": "DEFI Top 5 Tokens Index",
|
||||
"DEFIL": "DeFIL",
|
||||
@ -2162,11 +2185,12 @@
|
||||
"DIEM": "Facebook Diem",
|
||||
"DIESEL": "Diesel",
|
||||
"DIFX": "Digital Financial Exchange",
|
||||
"DIG": "Dignity",
|
||||
"DIG": "DIEGO",
|
||||
"DIGG": "DIGG",
|
||||
"DIGIC": "DigiCube",
|
||||
"DIGIF": "DigiFel",
|
||||
"DIGITAL": "Digital Reserve Currency",
|
||||
"DIGNITY": "Dignity",
|
||||
"DIGS": "Diggits",
|
||||
"DIKO": "Arkadiko",
|
||||
"DILI": "D Community",
|
||||
@ -2246,6 +2270,7 @@
|
||||
"DOGBOSS": "Dog Boss",
|
||||
"DOGDEFI": "DogDeFiCoin",
|
||||
"DOGE": "Dogecoin",
|
||||
"DOGE20": "Doge 2.0",
|
||||
"DOGEBNB": "DogeBNB",
|
||||
"DOGEC": "DogeCash",
|
||||
"DOGECEO": "Doge CEO",
|
||||
@ -2559,6 +2584,7 @@
|
||||
"EMC2": "Einsteinium",
|
||||
"EMD": "Emerald",
|
||||
"EMIGR": "EmiratesGoldCoin",
|
||||
"EML": "EML Protocol",
|
||||
"EMN.CUR": "Eastman Chemical",
|
||||
"EMON": "Ethermon",
|
||||
"EMOT": "Sentigraph.io",
|
||||
@ -2692,6 +2718,7 @@
|
||||
"ETHD": "Ethereum Dark",
|
||||
"ETHER": "Etherparty",
|
||||
"ETHERDELTA": "EtherDelta",
|
||||
"ETHERKING": "Ether Kingdoms Token",
|
||||
"ETHERNITY": "Ethernity Chain",
|
||||
"ETHF": "EthereumFair",
|
||||
"ETHIX": "EthicHub",
|
||||
@ -2709,6 +2736,7 @@
|
||||
"ETHSHIB": "Eth Shiba",
|
||||
"ETHV": "Ethverse",
|
||||
"ETHW": "Ethereum PoW",
|
||||
"ETHX": "Stader ETHx",
|
||||
"ETHY": "Ethereum Yield",
|
||||
"ETI": "EtherInc",
|
||||
"ETK": "Energi Token",
|
||||
@ -2722,7 +2750,7 @@
|
||||
"ETR": "Electric Token",
|
||||
"ETRNT": "Eternal Trusts",
|
||||
"ETS": "ETH Share",
|
||||
"ETSC": "Ether star blockchain",
|
||||
"ETSC": "Ether star blockchain",
|
||||
"ETT": "EncryptoTel",
|
||||
"ETY": "Ethereum Cloud",
|
||||
"ETZ": "EtherZero",
|
||||
@ -2773,6 +2801,7 @@
|
||||
"EXB": "ExaByte (EXB)",
|
||||
"EXC": "Eximchain",
|
||||
"EXCC": "ExchangeCoin",
|
||||
"EXCHANGEN": "ExchangeN",
|
||||
"EXCL": "Exclusive Coin",
|
||||
"EXE": "ExeCoin",
|
||||
"EXFI": "Flare Finance",
|
||||
@ -2781,7 +2810,7 @@
|
||||
"EXLT": "ExtraLovers",
|
||||
"EXM": "EXMO Coin",
|
||||
"EXMR": "EXMR FDN",
|
||||
"EXN": "ExchangeN",
|
||||
"EXN": "Exeno",
|
||||
"EXO": "Exosis",
|
||||
"EXP": "Expanse",
|
||||
"EXRD": "Radix",
|
||||
@ -2814,6 +2843,7 @@
|
||||
"FAIR": "FairCoin",
|
||||
"FAIRC": "Faireum Token",
|
||||
"FAIRG": "FairGame",
|
||||
"FAKE": "FAKE COIN",
|
||||
"FAKT": "Medifakt",
|
||||
"FALCONS": "Falcon Swaps",
|
||||
"FAME": "Fame MMA",
|
||||
@ -2870,6 +2900,7 @@
|
||||
"FEN": "First Ever NFT",
|
||||
"FENOMY": "Fenomy",
|
||||
"FER": "Ferro",
|
||||
"FERC": "FairERC20",
|
||||
"FERMA": "Ferma",
|
||||
"FESS": "Fesschain",
|
||||
"FET": "Fetch.AI",
|
||||
@ -2931,7 +2962,7 @@
|
||||
"FLASH": "Flashstake",
|
||||
"FLASHC": "FLASH coin",
|
||||
"FLC": "FlowChainCoin",
|
||||
"FLD": "FLUID",
|
||||
"FLD": "FluidAI",
|
||||
"FLDC": "Folding Coin",
|
||||
"FLDT": "FairyLand",
|
||||
"FLETA": "FLETA",
|
||||
@ -3091,6 +3122,7 @@
|
||||
"FUEL": "Jetfuel Finance",
|
||||
"FUJIN": "Fujinto",
|
||||
"FUKU": "Furukuru",
|
||||
"FUMO": "Alien Milady Fumo",
|
||||
"FUN": "FUN Token",
|
||||
"FUNC": "FunCoin",
|
||||
"FUND": "Unification",
|
||||
@ -3101,6 +3133,7 @@
|
||||
"FUNDZ": "FundFantasy",
|
||||
"FUNK": "Cypherfunks Coin",
|
||||
"FUR": "Furio",
|
||||
"FURU": "Furucombo",
|
||||
"FURY": "Engines of Fury",
|
||||
"FUS": "Fus",
|
||||
"FUSE": "Fuse Network Token",
|
||||
@ -3118,6 +3151,7 @@
|
||||
"FXP": "FXPay",
|
||||
"FXS": "Frax Share",
|
||||
"FXT": "FuzeX",
|
||||
"FXY": "Floxypay",
|
||||
"FYN": "Affyn",
|
||||
"FYP": "FlypMe",
|
||||
"FYZ": "Fyooz",
|
||||
@ -3172,6 +3206,7 @@
|
||||
"GAT": "GATCOIN",
|
||||
"GATE": "GATENet",
|
||||
"GATEWAY": "Gateway Protocol",
|
||||
"GAYPEPE": "Gay Pepe",
|
||||
"GAZE": "GazeTV",
|
||||
"GB": "GoldBlocks",
|
||||
"GBA": "Geeba",
|
||||
@ -3222,6 +3257,7 @@
|
||||
"GEMZ": "Gemz Social",
|
||||
"GEN": "DAOstack",
|
||||
"GENE": "Genopets",
|
||||
"GENIE": "The Genie",
|
||||
"GENIX": "Genix",
|
||||
"GENS": "Genshiro",
|
||||
"GENSTAKE": "Genstake",
|
||||
@ -3261,6 +3297,7 @@
|
||||
"GHCOLD": "Galaxy Heroes Coin",
|
||||
"GHD": "Giftedhands",
|
||||
"GHNY": "Grizzly Honey",
|
||||
"GHO": "GHO",
|
||||
"GHOST": "GhostbyMcAfee",
|
||||
"GHOSTCOIN": "GhostCoin",
|
||||
"GHOSTM": "GhostMarket",
|
||||
@ -3274,6 +3311,7 @@
|
||||
"GIFT": "GiftNet",
|
||||
"GIG": "GigaCoin",
|
||||
"GIGA": "GigaSwap",
|
||||
"GIGX": "GigXCoin",
|
||||
"GIM": "Gimli",
|
||||
"GIMMER": "Gimmer",
|
||||
"GIN": "GINcoin",
|
||||
@ -3385,6 +3423,7 @@
|
||||
"GOVT": "The Government Network",
|
||||
"GOZ": "Göztepe S.K. Fan Token",
|
||||
"GP": "Wizards And Dragons",
|
||||
"GPBP": "Genius Playboy Billionaire Philanthropist",
|
||||
"GPKR": "Gold Poker",
|
||||
"GPL": "Gold Pressed Latinum",
|
||||
"GPPT": "Pluto Project Coin",
|
||||
@ -3501,7 +3540,8 @@
|
||||
"HALF": "0.5X Long Bitcoin Token",
|
||||
"HALFSHIT": "0.5X Long Shitcoin Index Token",
|
||||
"HALLO": "Halloween Coin",
|
||||
"HALO": "Halo Platform",
|
||||
"HALO": "Halo Coin",
|
||||
"HALOPLATFORM": "Halo Platform",
|
||||
"HAM": "Hamster",
|
||||
"HAMS": "HamsterCoin",
|
||||
"HANA": "Hanacoin",
|
||||
@ -3598,6 +3638,7 @@
|
||||
"HILL": "President Clinton",
|
||||
"HINA": "Hina Inu",
|
||||
"HINT": "Hintchain",
|
||||
"HIPPO": "HIPPO",
|
||||
"HIRE": "HireMatch",
|
||||
"HIT": "HitChain",
|
||||
"HITBTC": "HitBTC Token",
|
||||
@ -3634,6 +3675,7 @@
|
||||
"HNTR": "Hunter",
|
||||
"HNY": "Honey",
|
||||
"HNZO": "Hanzo Inu",
|
||||
"HOBO": "HOBO THE BEAR",
|
||||
"HOD": "HoDooi.com",
|
||||
"HODL": "HOdlcoin",
|
||||
"HOGE": "Hoge Finance",
|
||||
@ -3839,7 +3881,7 @@
|
||||
"IMPCN": "Brain Space",
|
||||
"IMPER": "Impermax",
|
||||
"IMPS": "Impulse Coin",
|
||||
"IMPT": "Ether Kingdoms Token",
|
||||
"IMPT": "IMPT",
|
||||
"IMPULSE": "IMPULSE by FDR",
|
||||
"IMS": "Independent Money System",
|
||||
"IMST": "Imsmart",
|
||||
@ -4001,6 +4043,7 @@
|
||||
"JAM": "Tune.Fm",
|
||||
"JANE": "JaneCoin",
|
||||
"JAR": "Jarvis+",
|
||||
"JARED": "Jared From Subway",
|
||||
"JASMY": "JasmyCoin",
|
||||
"JBS": "JumBucks Coin",
|
||||
"JBX": "Juicebox",
|
||||
@ -4163,9 +4206,10 @@
|
||||
"KIN": "Kin",
|
||||
"KIND": "Kind Ads",
|
||||
"KINE": "Kine Protocol",
|
||||
"KING": "King Finance",
|
||||
"KING": "KING",
|
||||
"KING93": "King93",
|
||||
"KINGDOMQUEST": "Kingdom Quest",
|
||||
"KINGF": "King Finance",
|
||||
"KINGSHIB": "King Shiba",
|
||||
"KINGSWAP": "KingSwap",
|
||||
"KINT": "Kintsugi",
|
||||
@ -4175,6 +4219,7 @@
|
||||
"KISC": "Kaiser",
|
||||
"KISHIMOTO": "Kishimoto Inu",
|
||||
"KISHU": "Kishu Inu",
|
||||
"KITA": "KITA INU",
|
||||
"KITSU": "Kitsune Inu",
|
||||
"KITTY": "Kitty Inu",
|
||||
"KKO": "Kineko",
|
||||
@ -4267,10 +4312,12 @@
|
||||
"KUBO": "KUBO",
|
||||
"KUBOS": "KubosCoin",
|
||||
"KUE": "Kuende",
|
||||
"KUJI": "Kujira",
|
||||
"KUMA": "Kuma Inu",
|
||||
"KUNCI": "Kunci Coin",
|
||||
"KUR": "Kuro",
|
||||
"KURT": "Kurrent",
|
||||
"KUSA": "Kusa Inu",
|
||||
"KUSD": "Kowala",
|
||||
"KUSH": "KushCoin",
|
||||
"KUV": "Kuverit",
|
||||
@ -4280,6 +4327,7 @@
|
||||
"KVT": "Kinesis Velocity Token",
|
||||
"KWATT": "4New",
|
||||
"KWD": "KIWI DEFI",
|
||||
"KWENTA": "Kwenta",
|
||||
"KWH": "KWHCoin",
|
||||
"KWIK": "KwikSwap",
|
||||
"KWS": "Knight War Spirits",
|
||||
@ -4299,7 +4347,9 @@
|
||||
"LABX": "Stakinglab",
|
||||
"LACCOIN": "LocalAgro",
|
||||
"LACE": "Lovelace World",
|
||||
"LADYS": "Milady Meme Coin",
|
||||
"LAEEB": "LaEeb",
|
||||
"LAELAPS": "Laelaps",
|
||||
"LAIKA": "Laika Protocol",
|
||||
"LALA": "LaLa World",
|
||||
"LAMB": "Lambda",
|
||||
@ -4455,13 +4505,14 @@
|
||||
"LLAND": "Lyfe Land",
|
||||
"LLG": "Loligo",
|
||||
"LLION": "Lydian Lion",
|
||||
"LM": "LM Token",
|
||||
"LM": "LeisureMeta",
|
||||
"LMAO": "LMAO Finance",
|
||||
"LMC": "LomoCoin",
|
||||
"LMCH": "Latamcash",
|
||||
"LMCSWAP": "LimoCoin SWAP",
|
||||
"LMR": "Lumerin",
|
||||
"LMT": "Lympo Market Token",
|
||||
"LMTOKEN": "LM Token",
|
||||
"LMXC": "LimonX",
|
||||
"LMY": "Lunch Money",
|
||||
"LN": "LINK",
|
||||
@ -4530,6 +4581,7 @@
|
||||
"LRG": "Largo Coin",
|
||||
"LRN": "Loopring [NEO]",
|
||||
"LSD": "LightSpeedCoin",
|
||||
"LSETH": "Liquid Staked ETH",
|
||||
"LSK": "Lisk",
|
||||
"LSP": "Lumenswap",
|
||||
"LSS": "Lossless",
|
||||
@ -4626,6 +4678,7 @@
|
||||
"MAEP": "Maester Protocol",
|
||||
"MAG": "Magnet",
|
||||
"MAGIC": "Magic",
|
||||
"MAGICF": "MagicFox",
|
||||
"MAHA": "MahaDAO",
|
||||
"MAI": "Mindsync",
|
||||
"MAID": "MaidSafe Coin",
|
||||
@ -4639,6 +4692,7 @@
|
||||
"MANDOX": "MandoX",
|
||||
"MANGA": "Manga Token",
|
||||
"MANNA": "Manna",
|
||||
"MANTLE": "Mantle",
|
||||
"MAP": "MAP Protocol",
|
||||
"MAPC": "MapCoin",
|
||||
"MAPE": "Mecha Morphing",
|
||||
@ -4672,6 +4726,7 @@
|
||||
"MATIC": "Polygon",
|
||||
"MATPAD": "MaticPad",
|
||||
"MATTER": "AntiMatter",
|
||||
"MAV": "Maverick Protocol",
|
||||
"MAX": "MaxCoin",
|
||||
"MAXR": "Max Revive",
|
||||
"MAY": "Theresa May Coin",
|
||||
@ -4776,6 +4831,7 @@
|
||||
"MESA": "MetaVisa",
|
||||
"MESG": "MESG",
|
||||
"MESH": "MeshBox",
|
||||
"MESSI": "MESSI COIN",
|
||||
"MET": "Metronome",
|
||||
"META": "Metadium",
|
||||
"METAC": "Metacoin",
|
||||
@ -4881,6 +4937,7 @@
|
||||
"MIODIO": "MIODIOCOIN",
|
||||
"MIOTA": "IOTA",
|
||||
"MIR": "Mirror Protocol",
|
||||
"MIRACLE": "MIRACLE",
|
||||
"MIRC": "MIR COIN",
|
||||
"MIS": "Mithril Share",
|
||||
"MISA": "Sangkara",
|
||||
@ -4938,7 +4995,6 @@
|
||||
"MNRB": "MoneyRebel",
|
||||
"MNS": "Monnos",
|
||||
"MNST": "MoonStarter",
|
||||
"MNT": "microNFT",
|
||||
"MNTC": "Manet Coin",
|
||||
"MNTG": "Monetas",
|
||||
"MNTL": "AssetMantle",
|
||||
@ -4967,6 +5023,7 @@
|
||||
"MOF": "Molecular Future (TRC20)",
|
||||
"MOFI": "MobiFi",
|
||||
"MOFOLD": "Molecular Future (ERC20)",
|
||||
"MOG": "Mog Coin",
|
||||
"MOGU": "Mogu",
|
||||
"MOGX": "Mogu",
|
||||
"MOI": "MyOwnItem",
|
||||
@ -4989,9 +5046,11 @@
|
||||
"MONEYIMT": "MoneyToken",
|
||||
"MONF": "Monfter",
|
||||
"MONG": "MongCoin",
|
||||
"MONG20": "Mongoose 2.0",
|
||||
"MONI": "Monsta Infinite",
|
||||
"MONK": "Monkey Project",
|
||||
"MONKEY": "Monkey",
|
||||
"MONKEYS": "Monkeys Token",
|
||||
"MONO": "MonoX",
|
||||
"MONONOKEINU": "Mononoke Inu",
|
||||
"MONS": "Monsters Clan",
|
||||
@ -5011,11 +5070,13 @@
|
||||
"MOONSHOT": "Moonshot",
|
||||
"MOOO": "Hashtagger",
|
||||
"MOOV": "dotmoovs",
|
||||
"MOOX": "Moox Protocol",
|
||||
"MOPS": "Mops",
|
||||
"MORA": "Meliora",
|
||||
"MORE": "More Coin",
|
||||
"MOS": "MOS Coin",
|
||||
"MOT": "Olympus Labs",
|
||||
"MOTG": "MetaOctagon",
|
||||
"MOTI": "Motion",
|
||||
"MOTO": "Motocoin",
|
||||
"MOV": "MovieCoin",
|
||||
@ -5076,6 +5137,7 @@
|
||||
"MSWAP": "MoneySwap",
|
||||
"MT": "MyToken",
|
||||
"MTA": "Meta",
|
||||
"MTB": "MetaBridge",
|
||||
"MTBC": "Metabolic",
|
||||
"MTC": "MEDICAL TOKEN CURRENCY",
|
||||
"MTCMN": "MTC Mesh",
|
||||
@ -5108,6 +5170,7 @@
|
||||
"MUE": "MonetaryUnit",
|
||||
"MULTI": "Multichain",
|
||||
"MULTIBOT": "Multibot",
|
||||
"MULTIV": "Multiverse",
|
||||
"MUN": "MUNcoin",
|
||||
"MUNCH": "Munch Token",
|
||||
"MUSD": "mStable USD",
|
||||
@ -5648,6 +5711,7 @@
|
||||
"OZP": "OZAPHYRE",
|
||||
"P202": "Project 202",
|
||||
"P2PS": "P2P Solutions Foundation",
|
||||
"PAAL": "PAAL AI",
|
||||
"PAC": "PAC Protocol",
|
||||
"PACOCA": "Pacoca",
|
||||
"PAD": "NearPad",
|
||||
@ -5736,6 +5800,7 @@
|
||||
"PEARL": "Pearl Finance",
|
||||
"PEC": "PeaceCoin",
|
||||
"PEEL": "Meta Apes",
|
||||
"PEEPA": "Peepa",
|
||||
"PEEPS": "The People’s Coin",
|
||||
"PEG": "PegNet",
|
||||
"PEGS": "PegShares",
|
||||
@ -5748,6 +5813,7 @@
|
||||
"PEOPLE": "ConstitutionDAO",
|
||||
"PEOS": "pEOS",
|
||||
"PEPE": "Pepe",
|
||||
"PEPE20": "Pepe 2.0",
|
||||
"PEPECASH": "Pepe Cash",
|
||||
"PEPPER": "Pepper Token",
|
||||
"PEPS": "PEPS Coin",
|
||||
@ -5822,6 +5888,7 @@
|
||||
"PINK": "PinkCoin",
|
||||
"PINKX": "PantherCoin",
|
||||
"PINMO": "Pinmo",
|
||||
"PINO": "Pinocchu",
|
||||
"PINU": "Piccolo Inu",
|
||||
"PIO": "Pioneershares",
|
||||
"PIPI": "Pippi Finance",
|
||||
@ -5885,6 +5952,7 @@
|
||||
"PLS": "Pulsechain",
|
||||
"PLSD": "PulseDogecoin",
|
||||
"PLSPAD": "PulsePad",
|
||||
"PLSX": "PulseX",
|
||||
"PLT": "Poollotto.finance",
|
||||
"PLTC": "PlatonCoin",
|
||||
"PLTX": "PlutusX",
|
||||
@ -5911,7 +5979,6 @@
|
||||
"PNK": "Kleros",
|
||||
"PNL": "True PNL",
|
||||
"PNODE": "Pinknode",
|
||||
"PNP": "LogisticsX",
|
||||
"PNT": "pNetwork Token",
|
||||
"PNX": "PhantomX",
|
||||
"PNY": "Peony Coin",
|
||||
@ -5927,6 +5994,7 @@
|
||||
"POINTS": "Cryptsy Points",
|
||||
"POK": "Pokmonsters",
|
||||
"POKEM": "Pokemonio",
|
||||
"POKEMON": "Pokemon",
|
||||
"POKER": "PokerCoin",
|
||||
"POKT": "Pocket Network",
|
||||
"POL": "Pool-X",
|
||||
@ -6010,6 +6078,7 @@
|
||||
"PRIME": "Echelon Prime",
|
||||
"PRIMECHAIN": "PrimeChain",
|
||||
"PRINT": "Printer.Finance",
|
||||
"PRINTERIUM": "Printerium",
|
||||
"PRINTS": "FingerprintsDAO",
|
||||
"PRISM": "Prism",
|
||||
"PRIX": "Privatix",
|
||||
@ -6033,7 +6102,7 @@
|
||||
"PROTON": "Proton",
|
||||
"PROUD": "PROUD Money",
|
||||
"PROXI": "PROXI",
|
||||
"PRP": "Papyrus",
|
||||
"PRP": "Pepe Prime",
|
||||
"PRPS": "Purpose",
|
||||
"PRPT": "Purple Token",
|
||||
"PRQ": "PARSIQ",
|
||||
@ -6042,7 +6111,7 @@
|
||||
"PRTG": "Pre-Retogeum",
|
||||
"PRV": "PrivacySwap",
|
||||
"PRVS": "Previse",
|
||||
"PRX": "Printerium",
|
||||
"PRX": "Parex",
|
||||
"PRXY": "Proxy",
|
||||
"PRY": "PRIMARY",
|
||||
"PSB": "Planet Sandbox",
|
||||
@ -6120,6 +6189,7 @@
|
||||
"PYRAM": "Pyram Token",
|
||||
"PYRK": "Pyrk",
|
||||
"PYT": "Payther",
|
||||
"PYUSD": "PayPal USD",
|
||||
"PZM": "Prizm",
|
||||
"Q1S": "Quantum1Net",
|
||||
"Q2C": "QubitCoin",
|
||||
@ -6178,6 +6248,7 @@
|
||||
"QUA": "Quantum Tech",
|
||||
"QUACK": "Rich Quack",
|
||||
"QUAM": "Quam Network",
|
||||
"QUANT": "Quant Finance",
|
||||
"QUARASHI": "Quarashi Network",
|
||||
"QUARTZ": "Sandclock",
|
||||
"QUASA": "Quasacoin",
|
||||
@ -6201,7 +6272,7 @@
|
||||
"RAC": "RAcoin",
|
||||
"RACA": "Radio Caca",
|
||||
"RACEFI": "RaceFi",
|
||||
"RAD": "Radicle",
|
||||
"RAD": "Radworks",
|
||||
"RADAR": "DappRadar",
|
||||
"RADI": "RadicalCoin",
|
||||
"RADIO": "RadioShack",
|
||||
@ -6220,7 +6291,7 @@
|
||||
"RAM": "Ramifi Protocol",
|
||||
"RAMP": "RAMP",
|
||||
"RANKER": "RankerDao",
|
||||
"RAP": "Rapture",
|
||||
"RAP": "Philosoraptor",
|
||||
"RAPDOGE": "RapDoge",
|
||||
"RARE": "SuperRare",
|
||||
"RARI": "Rarible",
|
||||
@ -6277,6 +6348,7 @@
|
||||
"REA": "Realisto",
|
||||
"REAL": "RealLink",
|
||||
"REALM": "Realm",
|
||||
"REALMS": "Realms of Ethernity",
|
||||
"REALPLATFORM": "REAL",
|
||||
"REALY": "Realy Metaverse",
|
||||
"REAP": "ReapChain",
|
||||
@ -6287,6 +6359,7 @@
|
||||
"RED": "RED TOKEN",
|
||||
"REDC": "RedCab",
|
||||
"REDCO": "Redcoin",
|
||||
"REDDIT": "Reddit",
|
||||
"REDI": "REDi",
|
||||
"REDLANG": "RED",
|
||||
"REDLC": "Redlight Chain",
|
||||
@ -6324,7 +6397,7 @@
|
||||
"REST": "Restore",
|
||||
"RET": "RealTract",
|
||||
"RETAIL": "Retail.Global",
|
||||
"RETH": "Realms of Ethernity",
|
||||
"RETH": "Rocket Pool ETH",
|
||||
"RETH2": "rETH2",
|
||||
"RETIRE": "Retire Token",
|
||||
"REU": "REUCOIN",
|
||||
@ -6351,6 +6424,7 @@
|
||||
"RGP": "Rigel Protocol",
|
||||
"RGT": "Rari Governance Token",
|
||||
"RHEA": "Rhea",
|
||||
"RHINO": "RHINO",
|
||||
"RHOC": "RChain",
|
||||
"RHP": "Rhypton Club",
|
||||
"RIC": "Riecoin",
|
||||
@ -6490,6 +6564,7 @@
|
||||
"RWE": "Real-World Evidence",
|
||||
"RWN": "Rowan Token",
|
||||
"RWS": "Robonomics Web Services",
|
||||
"RXD": "Radiant",
|
||||
"RXT": "RIMAUNANGIS",
|
||||
"RYC": "RoyalCoin",
|
||||
"RYCN": "RoyalCoin 2.0",
|
||||
@ -6564,6 +6639,7 @@
|
||||
"SBTC": "Super Bitcoin",
|
||||
"SC": "Siacoin",
|
||||
"SCA": "SiaClassic",
|
||||
"SCAM": "Scam Coin",
|
||||
"SCAP": "SafeCapital",
|
||||
"SCAR": "Velhalla",
|
||||
"SCASH": "SpaceCash",
|
||||
@ -6624,6 +6700,7 @@
|
||||
"SEER": "SEER",
|
||||
"SEI": "Sei",
|
||||
"SEL": "SelenCoin",
|
||||
"SELF": "SELFCrypto",
|
||||
"SEM": "Semux",
|
||||
"SEN": "Sentaro",
|
||||
"SENATE": "SENATE",
|
||||
@ -6665,6 +6742,7 @@
|
||||
"SGE": "Society of Galactic Exploration",
|
||||
"SGLY": "Singularity",
|
||||
"SGN": "Signals Network",
|
||||
"SGO": "SafuuGO",
|
||||
"SGOLD": "SpaceGold",
|
||||
"SGP": "SGPay",
|
||||
"SGR": "Sogur Currency",
|
||||
@ -6684,6 +6762,7 @@
|
||||
"SHEESH": "Sheesh it is bussin bussin",
|
||||
"SHEESHA": "Sheesha Finance",
|
||||
"SHELL": "Shell Token",
|
||||
"SHERA": "Shera Tokens",
|
||||
"SHFL": "SHUFFLE!",
|
||||
"SHFT": "Shyft Network",
|
||||
"SHI": "Shirtum",
|
||||
@ -6719,6 +6798,8 @@
|
||||
"SHR": "ShareToken",
|
||||
"SHREK": "ShrekCoin",
|
||||
"SHROOM": "Shroom.Finance",
|
||||
"SHROOMFOX": "Magic Shroom",
|
||||
"SHS": "SHEESH",
|
||||
"SHX": "Stronghold Token",
|
||||
"SI": "Siren",
|
||||
"SIB": "SibCoin",
|
||||
@ -7018,9 +7099,11 @@
|
||||
"STEN": "Steneum Coin",
|
||||
"STEP": "Step Finance",
|
||||
"STEPH": "Step Hero",
|
||||
"STEPR": "Step",
|
||||
"STEPS": "Steps",
|
||||
"STERLINGCOIN": "SterlingCoin",
|
||||
"STETH": "Staked Ether",
|
||||
"STEWIE": "Stewie Coin",
|
||||
"STEX": "STEX",
|
||||
"STF": "Structure Finance",
|
||||
"STFX": "STFX",
|
||||
@ -7055,7 +7138,7 @@
|
||||
"STR": "Sourceless",
|
||||
"STRAKS": "Straks",
|
||||
"STRAX": "Stratis",
|
||||
"STRAY": "Animal Token",
|
||||
"STRAY": "Stray Dog",
|
||||
"STREAM": "STREAMIT COIN",
|
||||
"STRIP": "Stripto",
|
||||
"STRK": "Strike",
|
||||
@ -7361,6 +7444,7 @@
|
||||
"TOM": "TOM Finance",
|
||||
"TOMAHAWKCOIN": "Tomahawkcoin",
|
||||
"TOMB": "Tomb",
|
||||
"TOMI": "tomiNet",
|
||||
"TOMO": "TomoChain",
|
||||
"TOMOE": "TomoChain ERC20",
|
||||
"TOMS": "TomTomCoin",
|
||||
@ -7385,6 +7469,7 @@
|
||||
"TOTM": "Totem",
|
||||
"TOWER": "Tower",
|
||||
"TOWN": "Town Star",
|
||||
"TOX": "INTOverse",
|
||||
"TOZ": "Tozex",
|
||||
"TP": "Token Swap",
|
||||
"TPAD": "TrustPad",
|
||||
@ -7600,6 +7685,7 @@
|
||||
"UNITY": "SuperNET",
|
||||
"UNIVRS": "Universe",
|
||||
"UNIX": "UniX",
|
||||
"UNLEASH": "UnleashClub",
|
||||
"UNN": "UNION Protocol Governance Token",
|
||||
"UNO": "Unobtanium",
|
||||
"UNORE": "UnoRe",
|
||||
@ -7673,6 +7759,7 @@
|
||||
"UTT": "United Traders Token",
|
||||
"UTU": "UTU Protocol",
|
||||
"UUU": "U Network",
|
||||
"UWU": "uwu",
|
||||
"UZUMAKI": "Uzumaki Inu",
|
||||
"VAB": "Vabble",
|
||||
"VADER": "Vader Protocol",
|
||||
@ -7695,6 +7782,7 @@
|
||||
"VCF": "Valencia CF Fan Token",
|
||||
"VCG": "VCGamers",
|
||||
"VCK": "28VCK",
|
||||
"VCORE": "VCORE",
|
||||
"VDG": "VeriDocGlobal",
|
||||
"VDL": "Vidulum",
|
||||
"VDO": "VidioCoin",
|
||||
@ -7710,6 +7798,7 @@
|
||||
"VEIL": "VEIL",
|
||||
"VELA": "Vela Token",
|
||||
"VELO": "Velo",
|
||||
"VELOD": "Velodrome Finance",
|
||||
"VELOX": "Velox",
|
||||
"VELOXPROJECT": "Velox",
|
||||
"VEMP": "vEmpire DDAO",
|
||||
@ -7782,6 +7871,7 @@
|
||||
"VNT": "VNT Chain",
|
||||
"VNTW": "Value Network Token",
|
||||
"VNX": "VisionX",
|
||||
"VNXAU": "VNX Gold",
|
||||
"VNXLU": "VNX Exchange",
|
||||
"VOCO": "Provoco",
|
||||
"VODKA": "Vodka Token",
|
||||
@ -7902,7 +7992,8 @@
|
||||
"WEC": "Whole Earth Coin",
|
||||
"WEGEN": "WeGen Platform",
|
||||
"WELD": "Weld",
|
||||
"WELL": "Well",
|
||||
"WELL": "Moonwell",
|
||||
"WELLTOKEN": "Well",
|
||||
"WELT": "Fabwelt",
|
||||
"WELUPS": "Welups Blockchain",
|
||||
"WEMIX": "WEMIX",
|
||||
@ -7958,6 +8049,7 @@
|
||||
"WIX": "Wixlar",
|
||||
"WIZ": "WIZ Protocol",
|
||||
"WKD": "Wakanda Inu",
|
||||
"WLD": "Worldcoin",
|
||||
"WLF": "Wolfs Group",
|
||||
"WLITI": "wLITI",
|
||||
"WLK": "Wolk",
|
||||
@ -7983,6 +8075,7 @@
|
||||
"WNZ": "Winerz",
|
||||
"WOA": "Wrapped Origin Axie",
|
||||
"WOD": "World of Defish",
|
||||
"WOID": "WORLD ID",
|
||||
"WOJ": "Wojak Finance",
|
||||
"WOLF": "Insanity Coin",
|
||||
"WOLFILAND": "Wolfiland",
|
||||
@ -8000,6 +8093,7 @@
|
||||
"WOOFY": "Woofy",
|
||||
"WOOL": "Wolf Game Wool",
|
||||
"WOONK": "Woonkly",
|
||||
"WOOO": "wooonen",
|
||||
"WOOP": "Woonkly Power",
|
||||
"WOP": "WorldPay",
|
||||
"WORLD": "World Token",
|
||||
@ -8010,6 +8104,7 @@
|
||||
"WOZX": "Efforce",
|
||||
"WPC": "WePiggy Coin",
|
||||
"WPE": "OPES (Wrapped PE)",
|
||||
"WPLS": "Wrapped Pulse",
|
||||
"WPP": "Green Energy Token",
|
||||
"WPR": "WePower",
|
||||
"WQT": "Work Quest",
|
||||
@ -8049,6 +8144,7 @@
|
||||
"WZEC": "Wrapped Zcash",
|
||||
"WZENIQ": "Wrapped Zeniq (ETH)",
|
||||
"WZRD": "Wizardia",
|
||||
"X": "AI-X",
|
||||
"X2": "X2Coin",
|
||||
"X2Y2": "X2Y2",
|
||||
"X42": "X42 Protocol",
|
||||
@ -8096,7 +8192,7 @@
|
||||
"XCI": "Cannabis Industry Coin",
|
||||
"XCLR": "ClearCoin",
|
||||
"XCM": "CoinMetro",
|
||||
"XCN": "Chain",
|
||||
"XCN": "Onyxcoin",
|
||||
"XCO": "XCoin",
|
||||
"XCONSOL": "X-Consoles",
|
||||
"XCP": "CounterParty",
|
||||
@ -8365,6 +8461,7 @@
|
||||
"YUANG": "Yuang Coin",
|
||||
"YUCJ": "Yu Coin",
|
||||
"YUCT": "Yucreat",
|
||||
"YUDI": "Yudi",
|
||||
"YUM": "Yumerium",
|
||||
"YUMMY": "Yummy",
|
||||
"YUP": "Crowdholding",
|
||||
|
@ -142,6 +142,10 @@
|
||||
<loc>https://ghostfol.io/en/blog/2023/07/exploring-the-path-to-fire</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/blog/2023/08/ghostfolio-joins-oss-friends</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/en/faq</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
@ -322,6 +326,10 @@
|
||||
<loc>https://ghostfol.io/es/sobre/licencia</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/es/sobre/oss-friends</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/es/sobre/politica-de-privacidad</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
@ -342,6 +350,10 @@
|
||||
<loc>https://ghostfol.io/fr/a-propos/licence</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/fr/a-propos/oss-friends</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/fr/a-propos/politique-de-confidentialite</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
@ -396,12 +408,16 @@
|
||||
<loc>https://ghostfol.io/it/informazioni-su/changelog</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/it/informazioni-su/informativa-sulla-privacy</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/it/informazioni-su/licenza</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/it/informazioni-su/informativa-sulla-privacy</loc>
|
||||
<loc>https://ghostfol.io/it/informazioni-su/oss-friends</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
@ -460,6 +476,10 @@
|
||||
<loc>https://ghostfol.io/nl/over/licentie</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/nl/over/oss-friends</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/nl/over/privacybeleid</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
@ -520,6 +540,10 @@
|
||||
<loc>https://ghostfol.io/pt/sobre/licenca</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/pt/sobre/oss-friends</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://ghostfol.io/pt/sobre/politica-de-privacidade</loc>
|
||||
<lastmod>${currentDate}T00:00:00+00:00</lastmod>
|
||||
|
@ -71,6 +71,10 @@ const locales = {
|
||||
'/en/blog/2023/07/exploring-the-path-to-fire': {
|
||||
featureGraphicPath: 'assets/images/blog/20230701.jpg',
|
||||
title: `Exploring the Path to FIRE - ${titleShort}`
|
||||
},
|
||||
'/en/blog/2023/08/ghostfolio-joins-oss-friends': {
|
||||
featureGraphicPath: 'assets/images/blog/ghostfolio-joins-oss-friends.png',
|
||||
title: `Ghostfolio joins OSS Friends - ${titleShort}`
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -127,12 +127,10 @@ export class DataGatheringService {
|
||||
uniqueAssets = await this.getUniqueAssets();
|
||||
}
|
||||
|
||||
const assetProfiles = await this.dataProviderService.getAssetProfiles(
|
||||
uniqueAssets
|
||||
);
|
||||
const symbolProfiles = await this.symbolProfileService.getSymbolProfiles(
|
||||
uniqueAssets
|
||||
);
|
||||
const assetProfiles =
|
||||
await this.dataProviderService.getAssetProfiles(uniqueAssets);
|
||||
const symbolProfiles =
|
||||
await this.symbolProfileService.getSymbolProfiles(uniqueAssets);
|
||||
|
||||
for (const [symbol, assetProfile] of Object.entries(assetProfiles)) {
|
||||
const symbolMapping = symbolProfiles.find((symbolProfile) => {
|
||||
|
@ -4,14 +4,18 @@ import { TrackinsightDataEnhancerService } from '@ghostfolio/api/services/data-p
|
||||
import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service';
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { DataEnhancerService } from './data-enhancer.service';
|
||||
|
||||
@Module({
|
||||
exports: [
|
||||
'DataEnhancers',
|
||||
DataEnhancerService,
|
||||
TrackinsightDataEnhancerService,
|
||||
YahooFinanceDataEnhancerService
|
||||
YahooFinanceDataEnhancerService,
|
||||
'DataEnhancers'
|
||||
],
|
||||
imports: [ConfigurationModule, CryptocurrencyModule],
|
||||
providers: [
|
||||
DataEnhancerService,
|
||||
TrackinsightDataEnhancerService,
|
||||
YahooFinanceDataEnhancerService,
|
||||
{
|
||||
|
@ -0,0 +1,44 @@
|
||||
import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface';
|
||||
import { HttpException, Inject, Injectable } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||
|
||||
@Injectable()
|
||||
export class DataEnhancerService {
|
||||
public constructor(
|
||||
@Inject('DataEnhancers')
|
||||
private readonly dataEnhancers: DataEnhancerInterface[]
|
||||
) {}
|
||||
|
||||
public async enhance(aName: string) {
|
||||
const dataEnhancer = this.dataEnhancers.find((dataEnhancer) => {
|
||||
return dataEnhancer.getName() === aName;
|
||||
});
|
||||
|
||||
if (!dataEnhancer) {
|
||||
throw new HttpException(
|
||||
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||
StatusCodes.NOT_FOUND
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const assetProfile = await dataEnhancer.enhance({
|
||||
response: {
|
||||
assetClass: 'EQUITY',
|
||||
assetSubClass: 'ETF'
|
||||
},
|
||||
symbol: dataEnhancer.getTestSymbol()
|
||||
});
|
||||
|
||||
if (
|
||||
(assetProfile.countries as unknown as Prisma.JsonArray)?.length > 0 &&
|
||||
(assetProfile.sectors as unknown as Prisma.JsonArray)?.length > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import got from 'got';
|
||||
|
||||
@Injectable()
|
||||
export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
||||
private static baseUrl = 'https://data.trackinsight.com';
|
||||
private static baseUrl = 'https://www.trackinsight.com/data-api';
|
||||
private static countries = require('countries-list/dist/countries.json');
|
||||
private static countriesMapping = {
|
||||
'Russian Federation': 'Russia'
|
||||
@ -33,14 +33,22 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
||||
}
|
||||
|
||||
const profile = await got(
|
||||
`${TrackinsightDataEnhancerService.baseUrl}/data-api/funds/${symbol}.json`
|
||||
`${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol}.json`
|
||||
)
|
||||
.json<any>()
|
||||
.catch(() => {
|
||||
return {};
|
||||
return got(
|
||||
`${TrackinsightDataEnhancerService.baseUrl}/funds/${symbol.split(
|
||||
'.'
|
||||
)?.[0]}.json`
|
||||
)
|
||||
.json<any>()
|
||||
.catch(() => {
|
||||
return {};
|
||||
});
|
||||
});
|
||||
|
||||
const isin = profile.isin?.split(';')?.[0];
|
||||
const isin = profile?.isin?.split(';')?.[0];
|
||||
|
||||
if (isin) {
|
||||
response.isin = isin;
|
||||
@ -52,10 +60,14 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
||||
.json<any>()
|
||||
.catch(() => {
|
||||
return got(
|
||||
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${
|
||||
symbol.split('.')?.[0]
|
||||
}.json`
|
||||
);
|
||||
`${TrackinsightDataEnhancerService.baseUrl}/holdings/${symbol.split(
|
||||
'.'
|
||||
)?.[0]}.json`
|
||||
)
|
||||
.json<any>()
|
||||
.catch(() => {
|
||||
return {};
|
||||
});
|
||||
});
|
||||
|
||||
if (holdings?.weight < 0.95) {
|
||||
@ -114,4 +126,8 @@ export class TrackinsightDataEnhancerService implements DataEnhancerInterface {
|
||||
public getName() {
|
||||
return 'TRACKINSIGHT';
|
||||
}
|
||||
|
||||
public getTestSymbol() {
|
||||
return 'QQQ';
|
||||
}
|
||||
}
|
||||
|
@ -99,9 +99,8 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
||||
yahooSymbol = quotes[0].symbol;
|
||||
}
|
||||
|
||||
const { countries, sectors, url } = await this.getAssetProfile(
|
||||
yahooSymbol
|
||||
);
|
||||
const { countries, sectors, url } =
|
||||
await this.getAssetProfile(yahooSymbol);
|
||||
|
||||
if (countries) {
|
||||
response.countries = countries;
|
||||
@ -234,6 +233,10 @@ export class YahooFinanceDataEnhancerService implements DataEnhancerInterface {
|
||||
return DataSource.YAHOO;
|
||||
}
|
||||
|
||||
public getTestSymbol() {
|
||||
return 'AAPL';
|
||||
}
|
||||
|
||||
public parseAssetClass({
|
||||
quoteType,
|
||||
shortName
|
||||
|
@ -10,4 +10,6 @@ export interface DataEnhancerInterface {
|
||||
}): Promise<Partial<SymbolProfile>>;
|
||||
|
||||
getName(): string;
|
||||
|
||||
getTestSymbol(): string;
|
||||
}
|
||||
|
@ -65,9 +65,8 @@ export class TwitterBotService {
|
||||
status += benchmarkListing;
|
||||
}
|
||||
|
||||
const { data: createdTweet } = await this.twitterClient.v2.tweet(
|
||||
status
|
||||
);
|
||||
const { data: createdTweet } =
|
||||
await this.twitterClient.v2.tweet(status);
|
||||
|
||||
Logger.log(
|
||||
`Fear & Greed Index has been tweeted: https://twitter.com/ghostfolio_/status/${createdTweet.id}`,
|
||||
|
@ -1,22 +1,27 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes, TitleStrategy } from '@angular/router';
|
||||
import { routes as aboutRoutes } from '@ghostfolio/client/pages/about/routes';
|
||||
import { routes as faqRoutes } from '@ghostfolio/client/pages/faq/routes';
|
||||
import { routes as featuresRoutes } from '@ghostfolio/client/pages/features/routes';
|
||||
import { routes as marketsRoutes } from '@ghostfolio/client/pages/markets/routes';
|
||||
import { routes as pricingRoutes } from '@ghostfolio/client/pages/pricing/routes';
|
||||
import { routes as registerRoutes } from '@ghostfolio/client/pages/register/routes';
|
||||
import { routes as resourcesRoutes } from '@ghostfolio/client/pages/resources/routes';
|
||||
import { PageTitleStrategy } from '@ghostfolio/client/services/page-title.strategy';
|
||||
|
||||
import { ModulePreloadService } from './core/module-preload.service';
|
||||
|
||||
export const paths = {
|
||||
about: $localize`about`,
|
||||
faq: $localize`faq`,
|
||||
features: $localize`features`,
|
||||
license: $localize`license`,
|
||||
markets: $localize`markets`,
|
||||
pricing: $localize`pricing`,
|
||||
privacyPolicy: $localize`privacy-policy`,
|
||||
register: $localize`register`,
|
||||
resources: $localize`resources`
|
||||
};
|
||||
|
||||
const routes: Routes = [
|
||||
...aboutRoutes.map((path) => ({
|
||||
path,
|
||||
{
|
||||
path: paths.about,
|
||||
loadChildren: () =>
|
||||
import('./pages/about/about-page.module').then((m) => m.AboutPageModule)
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: 'account',
|
||||
loadChildren: () =>
|
||||
@ -51,30 +56,30 @@ const routes: Routes = [
|
||||
loadChildren: () =>
|
||||
import('./pages/demo/demo-page.module').then((m) => m.DemoPageModule)
|
||||
},
|
||||
...faqRoutes.map((path) => ({
|
||||
path,
|
||||
{
|
||||
path: paths.faq,
|
||||
loadChildren: () =>
|
||||
import('./pages/faq/faq-page.module').then((m) => m.FaqPageModule)
|
||||
})),
|
||||
...featuresRoutes.map((path) => ({
|
||||
path,
|
||||
},
|
||||
{
|
||||
path: paths.features,
|
||||
loadChildren: () =>
|
||||
import('./pages/features/features-page.module').then(
|
||||
(m) => m.FeaturesPageModule
|
||||
)
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
loadChildren: () =>
|
||||
import('./pages/home/home-page.module').then((m) => m.HomePageModule)
|
||||
},
|
||||
...marketsRoutes.map((path) => ({
|
||||
path,
|
||||
{
|
||||
path: paths.markets,
|
||||
loadChildren: () =>
|
||||
import('./pages/markets/markets-page.module').then(
|
||||
(m) => m.MarketsPageModule
|
||||
)
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: 'open',
|
||||
loadChildren: () =>
|
||||
@ -94,27 +99,27 @@ const routes: Routes = [
|
||||
(m) => m.PortfolioPageModule
|
||||
)
|
||||
},
|
||||
...pricingRoutes.map((path) => ({
|
||||
path,
|
||||
{
|
||||
path: paths.pricing,
|
||||
loadChildren: () =>
|
||||
import('./pages/pricing/pricing-page.module').then(
|
||||
(m) => m.PricingPageModule
|
||||
)
|
||||
})),
|
||||
...registerRoutes.map((path) => ({
|
||||
path,
|
||||
},
|
||||
{
|
||||
path: paths.register,
|
||||
loadChildren: () =>
|
||||
import('./pages/register/register-page.module').then(
|
||||
(m) => m.RegisterPageModule
|
||||
)
|
||||
})),
|
||||
...resourcesRoutes.map((path) => ({
|
||||
path,
|
||||
},
|
||||
{
|
||||
path: paths.resources,
|
||||
loadChildren: () =>
|
||||
import('./pages/resources/resources-page.module').then(
|
||||
(m) => m.ResourcesPageModule
|
||||
)
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: 'start',
|
||||
loadChildren: () =>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<a
|
||||
*ngIf="canCreateAccount"
|
||||
class="text-center"
|
||||
[routerLink]="['/register']"
|
||||
[routerLink]="routerLinkRegister"
|
||||
>
|
||||
<div
|
||||
class="cursor-pointer d-inline-block info-message px-3 py-2"
|
||||
@ -43,22 +43,7 @@
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
<footer
|
||||
*ngIf="
|
||||
(currentRoute === 'blog' ||
|
||||
currentRoute === 'faq' ||
|
||||
currentRoute === 'features' ||
|
||||
currentRoute === 'markets' ||
|
||||
currentRoute === 'open' ||
|
||||
currentRoute === 'p' ||
|
||||
currentRoute === 'pricing' ||
|
||||
currentRoute === 'resources' ||
|
||||
currentRoute === 'register' ||
|
||||
currentRoute === 'start') &&
|
||||
deviceType !== 'mobile'
|
||||
"
|
||||
class="d-flex justify-content-center py-4 w-100"
|
||||
>
|
||||
<footer *ngIf="showFooter" class="d-flex justify-content-center py-4 w-100">
|
||||
<div class="container">
|
||||
<div class="mb-3 row">
|
||||
<div class="col-sm">
|
||||
@ -68,36 +53,38 @@
|
||||
<div class="h6 mt-2" i18n>Personal Finance</div>
|
||||
<ul class="list-unstyled">
|
||||
<li *ngIf="hasPermissionToAccessFearAndGreedIndex">
|
||||
<a i18n [routerLink]="['/markets']">Markets</a>
|
||||
<a i18n [routerLink]="routerLinkMarkets">Markets</a>
|
||||
</li>
|
||||
<li><a i18n [routerLink]="['/resources']">Resources</a></li>
|
||||
<li><a i18n [routerLink]="routerLinkResources">Resources</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="h6 mt-2">Ghostfolio</div>
|
||||
<ul class="list-unstyled">
|
||||
<li><a i18n [routerLink]="['/about']">About</a></li>
|
||||
<li><a i18n [routerLink]="routerLinkAbout">About</a></li>
|
||||
<li *ngIf="hasPermissionForBlog">
|
||||
<a i18n [routerLink]="['/blog']">Blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a i18n [routerLink]="['/about', 'changelog']">Changelog</a>
|
||||
<a i18n [routerLink]="routerLinkAboutChangelog">Changelog</a>
|
||||
</li>
|
||||
<li><a i18n [routerLink]="['/features']">Features</a></li>
|
||||
<li><a i18n [routerLink]="routerLinkFeatures">Features</a></li>
|
||||
<li *ngIf="hasPermissionForSubscription">
|
||||
<a i18n [routerLink]="['/faq']">Frequently Asked Questions (FAQ)</a>
|
||||
<a i18n [routerLink]="routerLinkFaq"
|
||||
>Frequently Asked Questions (FAQ)</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a i18n [routerLink]="['/about', 'license']">License</a>
|
||||
<a i18n [routerLink]="routerLinkAboutLicense">License</a>
|
||||
</li>
|
||||
<li *ngIf="hasPermissionForStatistics">
|
||||
<a [routerLink]="['/open']">Open Startup</a>
|
||||
</li>
|
||||
<li *ngIf="hasPermissionForSubscription">
|
||||
<a i18n [routerLink]="['/pricing']">Pricing</a>
|
||||
<a i18n [routerLink]="routerLinkPricing">Pricing</a>
|
||||
</li>
|
||||
<li *ngIf="hasPermissionForSubscription">
|
||||
<a i18n [routerLink]="['/about', 'privacy-policy']"
|
||||
<a i18n [routerLink]="routerLinkAboutPrivacyPolicy"
|
||||
>Privacy Policy</a
|
||||
>
|
||||
</li>
|
||||
|
@ -38,6 +38,20 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
public hasPermissionToAccessFearAndGreedIndex: boolean;
|
||||
public info: InfoItem;
|
||||
public pageTitle: string;
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkAboutChangelog = ['/' + $localize`about`, 'changelog'];
|
||||
public routerLinkAboutLicense = ['/' + $localize`about`, $localize`license`];
|
||||
public routerLinkAboutPrivacyPolicy = [
|
||||
'/' + $localize`about`,
|
||||
$localize`privacy-policy`
|
||||
];
|
||||
public routerLinkFaq = ['/' + $localize`faq`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkMarkets = ['/' + $localize`markets`];
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
public routerLinkRegister = ['/' + $localize`register`];
|
||||
public routerLinkResources = ['/' + $localize`resources`];
|
||||
public showFooter = false;
|
||||
public user: User;
|
||||
public version = environment.version;
|
||||
|
||||
@ -89,6 +103,19 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
const urlSegments = urlSegmentGroup.segments;
|
||||
this.currentRoute = urlSegments[0].path;
|
||||
|
||||
this.showFooter =
|
||||
(this.currentRoute === 'blog' ||
|
||||
this.currentRoute === this.routerLinkFaq[0].slice(1) ||
|
||||
this.currentRoute === this.routerLinkFeatures[0].slice(1) ||
|
||||
this.currentRoute === this.routerLinkMarkets[0].slice(1) ||
|
||||
this.currentRoute === 'open' ||
|
||||
this.currentRoute === 'p' ||
|
||||
this.currentRoute === this.routerLinkPricing[0].slice(1) ||
|
||||
this.currentRoute === this.routerLinkRegister[0].slice(1) ||
|
||||
this.currentRoute === this.routerLinkResources[0].slice(1) ||
|
||||
this.currentRoute === 'start') &&
|
||||
this.deviceType !== 'mobile';
|
||||
|
||||
if (this.deviceType === 'mobile') {
|
||||
setTimeout(() => {
|
||||
const index = this.title.getTitle().indexOf('–');
|
||||
|
@ -29,7 +29,7 @@
|
||||
}"
|
||||
[title]="
|
||||
(itemByMonth.key + '-' + (i + 1 < 10 ? '0' + (i + 1) : i + 1)
|
||||
| date : defaultDateFormat) ?? ''
|
||||
| date: defaultDateFormat) ?? ''
|
||||
"
|
||||
(click)="
|
||||
onOpenMarketDataDetail({
|
||||
|
@ -69,10 +69,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'resources',
|
||||
'text-decoration-underline': currentRoute === 'resources'
|
||||
'font-weight-bold': currentRoute === routeResources,
|
||||
'text-decoration-underline': currentRoute === routeResources
|
||||
}"
|
||||
[routerLink]="['/resources']"
|
||||
[routerLink]="routerLinkResources"
|
||||
>Resources</a
|
||||
>
|
||||
</li>
|
||||
@ -87,10 +87,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'pricing',
|
||||
'text-decoration-underline': currentRoute === 'pricing'
|
||||
'font-weight-bold': currentRoute === routePricing,
|
||||
'text-decoration-underline': currentRoute === routePricing
|
||||
}"
|
||||
[routerLink]="['/pricing']"
|
||||
[routerLink]="routerLinkPricing"
|
||||
>Pricing</a
|
||||
>
|
||||
</li>
|
||||
@ -100,10 +100,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'about',
|
||||
'text-decoration-underline': currentRoute === 'about'
|
||||
'font-weight-bold': currentRoute === routeAbout,
|
||||
'text-decoration-underline': currentRoute === routeAbout
|
||||
}"
|
||||
[routerLink]="['/about']"
|
||||
[routerLink]="routerLinkAbout"
|
||||
>About</a
|
||||
>
|
||||
</li>
|
||||
@ -210,9 +210,9 @@
|
||||
i18n
|
||||
mat-menu-item
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'resources'
|
||||
'font-weight-bold': currentRoute === routeResources
|
||||
}"
|
||||
[routerLink]="['/resources']"
|
||||
[routerLink]="routerLinkResources"
|
||||
>Resources</a
|
||||
>
|
||||
<a
|
||||
@ -223,16 +223,16 @@
|
||||
class="d-flex d-sm-none"
|
||||
i18n
|
||||
mat-menu-item
|
||||
[ngClass]="{ 'font-weight-bold': currentRoute === 'pricing' }"
|
||||
[routerLink]="['/pricing']"
|
||||
[ngClass]="{ 'font-weight-bold': currentRoute === routePricing }"
|
||||
[routerLink]="routerLinkPricing"
|
||||
>Pricing</a
|
||||
>
|
||||
<a
|
||||
class="d-flex d-sm-none"
|
||||
i18n
|
||||
mat-menu-item
|
||||
[ngClass]="{ 'font-weight-bold': currentRoute === 'about' }"
|
||||
[routerLink]="['/about']"
|
||||
[ngClass]="{ 'font-weight-bold': currentRoute === routeAbout }"
|
||||
[routerLink]="routerLinkAbout"
|
||||
>About Ghostfolio</a
|
||||
>
|
||||
<hr class="d-flex d-sm-none m-0" />
|
||||
@ -260,10 +260,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'features',
|
||||
'text-decoration-underline': currentRoute === 'features'
|
||||
'font-weight-bold': currentRoute === routeFeatures,
|
||||
'text-decoration-underline': currentRoute === routeFeatuers
|
||||
}"
|
||||
[routerLink]="['/features']"
|
||||
[routerLink]="routerLinkFeatures"
|
||||
>Features</a
|
||||
>
|
||||
</li>
|
||||
@ -273,10 +273,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'about',
|
||||
'text-decoration-underline': currentRoute === 'about'
|
||||
'font-weight-bold': currentRoute === routeAbout,
|
||||
'text-decoration-underline': currentRoute === routeAbout
|
||||
}"
|
||||
[routerLink]="['/about']"
|
||||
[routerLink]="routerLinkAbout"
|
||||
>About</a
|
||||
>
|
||||
</li>
|
||||
@ -285,10 +285,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'pricing',
|
||||
'text-decoration-underline': currentRoute === 'pricing'
|
||||
'font-weight-bold': currentRoute === routePricing,
|
||||
'text-decoration-underline': currentRoute === routePricing
|
||||
}"
|
||||
[routerLink]="['/pricing']"
|
||||
[routerLink]="routerLinkPricing"
|
||||
>Pricing</a
|
||||
>
|
||||
</li>
|
||||
@ -301,10 +301,10 @@
|
||||
i18n
|
||||
mat-flat-button
|
||||
[ngClass]="{
|
||||
'font-weight-bold': currentRoute === 'markets',
|
||||
'text-decoration-underline': currentRoute === 'markets'
|
||||
'font-weight-bold': currentRoute === routeMarkets,
|
||||
'text-decoration-underline': currentRoute === routeMarkets
|
||||
}"
|
||||
[routerLink]="['/markets']"
|
||||
[routerLink]="routerLinkMarkets"
|
||||
>Markets</a
|
||||
>
|
||||
</li>
|
||||
@ -329,7 +329,7 @@
|
||||
class="d-none d-sm-block"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/register']"
|
||||
[routerLink]="routerLinkRegister"
|
||||
><ng-container i18n>Get started</ng-container>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -42,6 +42,17 @@ export class HeaderComponent implements OnChanges {
|
||||
public hasPermissionToCreateUser: boolean;
|
||||
public impersonationId: string;
|
||||
public isMenuOpen: boolean;
|
||||
public routeAbout = $localize`about`;
|
||||
public routeFeatures = $localize`features`;
|
||||
public routeMarkets = $localize`markets`;
|
||||
public routePricing = $localize`pricing`;
|
||||
public routeResources = $localize`resources`;
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkMarkets = ['/' + $localize`markets`];
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
public routerLinkRegister = ['/' + $localize`register`];
|
||||
public routerLinkResources = ['/' + $localize`resources`];
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
|
@ -121,7 +121,7 @@ export class HomeSummaryComponent implements OnDestroy, OnInit {
|
||||
});
|
||||
|
||||
this.snackBarRef.onAction().subscribe(() => {
|
||||
this.router.navigate(['/pricing']);
|
||||
this.router.navigate(['/' + $localize`pricing`]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ import { SubscriptionInterstitialDialogParams } from './interfaces/interfaces';
|
||||
templateUrl: 'subscription-interstitial-dialog.html'
|
||||
})
|
||||
export class SubscriptionInterstitialDialog {
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
|
||||
public constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public data: SubscriptionInterstitialDialogParams,
|
||||
public dialogRef: MatDialogRef<SubscriptionInterstitialDialog>
|
||||
|
@ -56,7 +56,7 @@
|
||||
<a
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/pricing']"
|
||||
[routerLink]="routerLinkPricing"
|
||||
(click)="closeDialog()"
|
||||
>
|
||||
<span i18n>Upgrade Plan</span>
|
||||
|
@ -4,13 +4,7 @@ import {
|
||||
Router,
|
||||
RouterStateSnapshot
|
||||
} from '@angular/router';
|
||||
import { routes as aboutRoutes } from '@ghostfolio/client/pages/about/routes';
|
||||
import { routes as faqRoutes } from '@ghostfolio/client/pages/faq/routes';
|
||||
import { routes as featuresRoutes } from '@ghostfolio/client/pages/features/routes';
|
||||
import { routes as marketsRoutes } from '@ghostfolio/client/pages/markets/routes';
|
||||
import { routes as pricingRoutes } from '@ghostfolio/client/pages/pricing/routes';
|
||||
import { routes as registerRoutes } from '@ghostfolio/client/pages/register/routes';
|
||||
import { routes as resourcesRoutes } from '@ghostfolio/client/pages/resources/routes';
|
||||
import { paths } from '@ghostfolio/client/app-routing.module';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
@ -20,17 +14,17 @@ import { catchError } from 'rxjs/operators';
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthGuard {
|
||||
private static PUBLIC_PAGE_ROUTES = [
|
||||
...aboutRoutes.map((route) => `/${route}`),
|
||||
`/${paths.about}`,
|
||||
'/blog',
|
||||
'/demo',
|
||||
...faqRoutes.map((route) => `/${route}`),
|
||||
...featuresRoutes.map((route) => `/${route}`),
|
||||
...marketsRoutes.map((route) => `/${route}`),
|
||||
`/${paths.faq}`,
|
||||
`/${paths.features}`,
|
||||
`/${paths.markets}`,
|
||||
'/open',
|
||||
'/p',
|
||||
...pricingRoutes.map((route) => `/${route}`),
|
||||
...registerRoutes.map((route) => `/${route}`),
|
||||
...resourcesRoutes.map((route) => `/${route}`)
|
||||
`/${paths.pricing}`,
|
||||
`/${paths.register}`,
|
||||
`/${paths.resources}`
|
||||
];
|
||||
|
||||
constructor(
|
||||
@ -56,7 +50,7 @@ export class AuthGuard {
|
||||
this.router.navigate(['/demo']);
|
||||
resolve(false);
|
||||
} else if (utmSource === 'trusted-web-activity') {
|
||||
this.router.navigate(['/register']);
|
||||
this.router.navigate(['/' + $localize`register`]);
|
||||
resolve(false);
|
||||
} else if (
|
||||
AuthGuard.PUBLIC_PAGE_ROUTES.filter((publicPageRoute) =>
|
||||
|
@ -77,7 +77,7 @@ export class HttpResponseInterceptor implements HttpInterceptor {
|
||||
});
|
||||
|
||||
this.snackBarRef.onAction().subscribe(() => {
|
||||
this.router.navigate(['/pricing']);
|
||||
this.router.navigate(['/' + $localize`pricing`]);
|
||||
});
|
||||
}
|
||||
} else if (error.status === StatusCodes.INTERNAL_SERVER_ERROR) {
|
||||
|
@ -1,5 +1,8 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { paths } from '@ghostfolio/client/app-routing.module';
|
||||
import { AuthGuard } from '@ghostfolio/client/core/auth.guard';
|
||||
|
||||
import { AboutPageComponent } from './about-page.component';
|
||||
@ -22,22 +25,13 @@ const routes: Routes = [
|
||||
(m) => m.ChangelogPageModule
|
||||
)
|
||||
},
|
||||
...[
|
||||
'license',
|
||||
/////
|
||||
'licenca',
|
||||
'licence',
|
||||
'licencia',
|
||||
'licentie',
|
||||
'lizenz',
|
||||
'licenza'
|
||||
].map((path) => ({
|
||||
path,
|
||||
{
|
||||
path: paths.license,
|
||||
loadChildren: () =>
|
||||
import('./license/license-page.module').then(
|
||||
(m) => m.LicensePageModule
|
||||
)
|
||||
})),
|
||||
},
|
||||
{
|
||||
path: 'oss-friends',
|
||||
loadChildren: () =>
|
||||
@ -45,22 +39,13 @@ const routes: Routes = [
|
||||
(m) => m.OpenSourceSoftwareFriendsPageModule
|
||||
)
|
||||
},
|
||||
...[
|
||||
'privacy-policy',
|
||||
/////
|
||||
'datenschutzbestimmungen',
|
||||
'informativa-sulla-privacy',
|
||||
'politique-de-confidentialite',
|
||||
'politica-de-privacidad',
|
||||
'politica-de-privacidade',
|
||||
'privacybeleid'
|
||||
].map((path) => ({
|
||||
path,
|
||||
{
|
||||
path: paths.privacyPolicy,
|
||||
loadChildren: () =>
|
||||
import('./privacy-policy/privacy-policy-page.module').then(
|
||||
(m) => m.PrivacyPolicyPageModule
|
||||
)
|
||||
}))
|
||||
}
|
||||
],
|
||||
component: AboutPageComponent,
|
||||
path: '',
|
||||
|
@ -48,17 +48,17 @@ export class AboutPageComponent implements OnDestroy, OnInit {
|
||||
{
|
||||
iconName: 'reader-outline',
|
||||
label: $localize`About`,
|
||||
path: ['/about']
|
||||
path: ['/' + $localize`about`]
|
||||
},
|
||||
{
|
||||
iconName: 'sparkles-outline',
|
||||
label: $localize`Changelog`,
|
||||
path: ['/about', 'changelog']
|
||||
path: ['/' + $localize`about`, 'changelog']
|
||||
},
|
||||
{
|
||||
iconName: 'ribbon-outline',
|
||||
label: $localize`License`,
|
||||
path: ['/about', 'license']
|
||||
path: ['/' + $localize`about`, $localize`license`]
|
||||
}
|
||||
];
|
||||
|
||||
@ -66,7 +66,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
|
||||
this.tabs.push({
|
||||
iconName: 'shield-checkmark-outline',
|
||||
label: $localize`Privacy Policy`,
|
||||
path: ['/about', 'privacy-policy'],
|
||||
path: ['/' + $localize`about`, $localize`privacy-policy`],
|
||||
showCondition: this.hasPermissionForSubscription
|
||||
});
|
||||
this.user = state.user;
|
||||
@ -83,7 +83,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
|
||||
this.tabs.push({
|
||||
iconName: 'happy-outline',
|
||||
label: 'OSS Friends',
|
||||
path: ['/about', 'oss-friends']
|
||||
path: ['/' + $localize`about`, 'oss-friends']
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
const ossFriends = require('../../../../assets/oss-friends.json');
|
||||
|
||||
@Component({
|
||||
host: { class: 'page' },
|
||||
selector: 'gf-oss-friends-page',
|
||||
@ -8,131 +10,7 @@ import { Subject } from 'rxjs';
|
||||
templateUrl: './oss-friends-page.html'
|
||||
})
|
||||
export class OpenSourceSoftwareFriendsPageComponent implements OnDestroy {
|
||||
public ossFriends = [
|
||||
{
|
||||
description: 'Build custom software on top of your data.',
|
||||
name: 'Appsmith',
|
||||
url: 'https://www.appsmith.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'BoxyHQ’s suite of APIs for security and privacy helps engineering teams build and ship compliant cloud applications faster.',
|
||||
name: 'BoxyHQ',
|
||||
url: 'https://boxyhq.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Cal.com is a scheduling tool that helps you schedule meetings without the back-and-forth emails.',
|
||||
name: 'Cal.com',
|
||||
url: 'https://cal.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Centralize community, product, and customer data to understand which companies are engaging with your open source project.',
|
||||
name: 'Crowd.dev',
|
||||
url: 'https://www.crowd.dev'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'The Open-Source DocuSign Alternative. We aim to earn your trust by enabling you to self-host the platform and examine its inner workings.',
|
||||
name: 'Documenso',
|
||||
url: 'https://documenso.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'The Open-Source HubSpot Alternative. A single XOS enables to create unique and life-changing experiences that work for all types of business.',
|
||||
name: 'Erxes',
|
||||
url: 'https://erxes.io'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Survey granular user segments at any point in the user journey. Gather up to 6x more insights with targeted micro-surveys. All open-source.',
|
||||
name: 'Formbricks',
|
||||
url: 'https://formbricks.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'GitWonk is an open-source technical documentation tool, designed and built focusing on the developer experience.',
|
||||
name: 'GitWonk',
|
||||
url: 'https://gitwonk.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Open-source authentication and user management for the passkey era. Integrated in minutes, for web and mobile apps.',
|
||||
name: 'Hanko',
|
||||
url: 'https://www.hanko.io'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'HTMX is a dependency-free JavaScript library that allows you to access AJAX, CSS Transitions, WebSockets, and Server Sent Events directly in HTML.',
|
||||
name: 'HTMX',
|
||||
url: 'https://htmx.org'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Open source, end-to-end encrypted platform that lets you securely manage secrets and configs across your team, devices, and infrastructure.',
|
||||
name: 'Infisical',
|
||||
url: 'https://infisical.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Mockoon is the easiest and quickest way to design and run mock REST APIs.',
|
||||
name: 'Mockoon',
|
||||
url: 'https://mockoon.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'The open-source notification infrastructure for developers. Simple components and APIs for managing all communication channels in one place.',
|
||||
name: 'Novu',
|
||||
url: 'https://novu.co'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Democratizing investment research through an open source financial ecosystem. The OpenBB Terminal allows everyone to perform investment research, from everywhere.',
|
||||
name: 'OpenBB',
|
||||
url: 'https://openbb.co'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Sniffnet is a network monitoring tool to help you easily keep track of your Internet traffic.',
|
||||
name: 'Sniffnet',
|
||||
url: 'https://www.sniffnet.net'
|
||||
},
|
||||
{
|
||||
description: 'Software localization from A to Z made really easy.',
|
||||
name: 'Tolgee',
|
||||
url: 'https://tolgee.io'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Create long-running Jobs directly in your codebase with features like API integrations, webhooks, scheduling and delays.',
|
||||
name: 'Trigger.dev',
|
||||
url: 'https://trigger.dev'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Typebot gives you powerful blocks to create unique chat experiences. Embed them anywhere on your apps and start collecting results like magic.',
|
||||
name: 'Typebot',
|
||||
url: 'https://typebot.io'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'A modern CRM offering the flexibility of open-source, advanced features and sleek design.',
|
||||
name: 'Twenty',
|
||||
url: 'https://twenty.com'
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Open-source enterprise-grade serverless CMS. Own your data. Scale effortlessly. Customize everything.',
|
||||
name: 'Webiny',
|
||||
url: 'https://www.webiny.com'
|
||||
},
|
||||
{
|
||||
description: 'Webstudio is an open source alternative to Webflow',
|
||||
name: 'Webstudio',
|
||||
url: 'https://webstudio.is'
|
||||
}
|
||||
];
|
||||
public ossFriends = ossFriends.data;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
><ng-container i18n>Our</ng-container> OSS Friends</span
|
||||
>
|
||||
<small class="text-muted" i18n
|
||||
>Discover more Open Source Software projects</small
|
||||
>Discover other exciting Open Source Software projects</small
|
||||
>
|
||||
</h1>
|
||||
<div class="row">
|
||||
@ -14,26 +14,27 @@
|
||||
*ngFor="let ossFriend of ossFriends"
|
||||
class="col-xs-12 col-md-4 mb-3"
|
||||
>
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
<mat-card-header>
|
||||
<mat-card-title class="h4"
|
||||
><a target="_blank" [href]="ossFriend.url"
|
||||
>{{ ossFriend.name }}</a
|
||||
></mat-card-title
|
||||
>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="flex-grow-1">
|
||||
<p>{{ ossFriend.description }}</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="justify-content-end">
|
||||
<a mat-button target="_blank" [href]="ossFriend.url">
|
||||
<span
|
||||
><ng-container i18n>Visit</ng-container> {{ ossFriend.name
|
||||
}}</span
|
||||
><ion-icon class="ml-1" name="arrow-forward-outline"></ion-icon>
|
||||
</a>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
<a target="_blank" [href]="ossFriend.href">
|
||||
<mat-card appearance="outlined" class="d-flex flex-column h-100">
|
||||
<mat-card-header>
|
||||
<mat-card-title class="h4">{{ ossFriend.name }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="flex-grow-1">
|
||||
<p>{{ ossFriend.description }}</p>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="justify-content-end">
|
||||
<a mat-button target="_blank" [href]="ossFriend.href">
|
||||
<span
|
||||
><ng-container i18n>Visit</ng-container> {{ ossFriend.name
|
||||
}}</span
|
||||
><ion-icon
|
||||
class="ml-1"
|
||||
name="arrow-forward-outline"
|
||||
></ion-icon>
|
||||
</a>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
||||
import { OpenSourceSoftwareFriendsPageRoutingModule } from './oss-friends-page-routing.module';
|
||||
import { OpenSourceSoftwareFriendsPageComponent } from './oss-friends-page.component';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
@NgModule({
|
||||
declarations: [OpenSourceSoftwareFriendsPageComponent],
|
||||
|
@ -1,3 +1,9 @@
|
||||
:host {
|
||||
display: block;
|
||||
|
||||
.mat-mdc-card {
|
||||
&:hover {
|
||||
border-color: var(--gf-theme-primary-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ export class AboutOverviewPageComponent implements OnDestroy, OnInit {
|
||||
public hasPermissionForStatistics: boolean;
|
||||
public hasPermissionForSubscription: boolean;
|
||||
public isLoggedIn: boolean;
|
||||
public routerLinkFaq = ['/' + $localize`faq`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public user: User;
|
||||
public version = environment.version;
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
<div class="container">
|
||||
<div class="mb-5 row">
|
||||
<div class="col">
|
||||
<h1 class="d-none d-sm-block h3 mb-4 text-center">About Ghostfolio</h1>
|
||||
<h1 class="d-none d-sm-block h3 mb-4 text-center">
|
||||
<ng-container i18n>About Ghostfolio</ng-container>
|
||||
</h1>
|
||||
<div class="about-container">
|
||||
<p>
|
||||
Ghostfolio is a lightweight wealth management application for
|
||||
@ -46,7 +48,7 @@
|
||||
<p>
|
||||
If you encounter a bug or would like to suggest an improvement or a
|
||||
new
|
||||
<a [routerLink]="['/features']">feature</a>, please join the
|
||||
<a [routerLink]="routerLinkFeatures">feature</a>, please join the
|
||||
Ghostfolio
|
||||
<a
|
||||
href="https://join.slack.com/t/ghostfolio/shared_invite/zt-vsaan64h-F_I0fEo5M0P88lP9ibCxFg"
|
||||
@ -139,7 +141,7 @@
|
||||
class="py-4 w-100"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
[routerLink]="['/faq']"
|
||||
[routerLink]="routerLinkFaq"
|
||||
>Frequently Asked Questions (FAQ)</a
|
||||
>
|
||||
</div>
|
||||
|
@ -1,9 +0,0 @@
|
||||
export const routes = [
|
||||
'about',
|
||||
/////
|
||||
'a-propos',
|
||||
'informazioni-su',
|
||||
'over',
|
||||
'sobre',
|
||||
'ueber-uns'
|
||||
] as const;
|
@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './hallo-ghostfolio-page.html'
|
||||
})
|
||||
export class HalloGhostfolioPageComponent {}
|
||||
export class HalloGhostfolioPageComponent {
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
public routerLinkResources = ['/' + $localize`resources`];
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
Aufgrund der steigenden Inflation und den Negativzinsen befasse ich
|
||||
mich seit einiger Zeit, wie ich mein Vermögen möglichst
|
||||
diversifiziert anlegen kann. Konkret verfolge ich eine
|
||||
<a [routerLink]="['/resources']">Buy and Hold Strategie</a> mit
|
||||
<a [routerLink]="routerLinkResources">Buy and Hold Strategie</a> mit
|
||||
Investitionen in verschiedene Anlageklassen verteilt auf
|
||||
unterschiedliche Plattformen. Deshalb suchte ich nach einer App, die
|
||||
mein Portfolio ganzheitlich zusammenfasst. Bei meiner
|
||||
@ -119,7 +119,7 @@
|
||||
Anlagestrategie? Ich freue mich über alle, die Ghostfolio
|
||||
ausprobieren. Bist du überzeugt vom Potential der Software? Jede
|
||||
Unterstützung für Ghostfolio ist willkommen. Sei es mit einer
|
||||
<a [routerLink]="['/pricing']">Ghostfolio Premium</a>
|
||||
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>
|
||||
Subscription zur Finanzierung des Hostings, einem positiven Rating
|
||||
im
|
||||
<a
|
||||
|
@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './hello-ghostfolio-page.html'
|
||||
})
|
||||
export class HelloGhostfolioPageComponent {}
|
||||
export class HelloGhostfolioPageComponent {
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
public routerLinkResources = ['/' + $localize`resources`];
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
Due to rising inflation and negative interest rates, I have been
|
||||
looking for some time at how I can invest my assets in the most
|
||||
diversified way possible. Specifically, I follow a
|
||||
<a [routerLink]="['/resources']">buy and hold strategy</a> with
|
||||
<a [routerLink]="routerLinkResources">buy and hold strategy</a> with
|
||||
investments in different asset classes spread across different
|
||||
platforms. Therefore, I was looking for an app that would
|
||||
holistically aggregate my portfolio. During my research on the
|
||||
@ -115,7 +115,7 @@
|
||||
strategy? I'm happy for everyone who tries Ghostfolio. Are you
|
||||
convinced of its potential? Any support for Ghostfolio is welcome.
|
||||
Be it with a
|
||||
<a [routerLink]="['/pricing']">Ghostfolio Premium</a>
|
||||
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>
|
||||
Subscription to finance the hosting, a positive rating in the
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"
|
||||
|
@ -9,4 +9,6 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './first-months-in-open-source-page.html'
|
||||
})
|
||||
export class FirstMonthsInOpenSourcePageComponent {}
|
||||
export class FirstMonthsInOpenSourcePageComponent {
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
}
|
||||
|
@ -86,7 +86,7 @@
|
||||
</p>
|
||||
<p>
|
||||
My personal goal is to reach break-even with the Saas offering (<a
|
||||
[routerLink]="['/pricing']"
|
||||
[routerLink]="routerLinkPricing"
|
||||
>Ghostfolio Premium</a
|
||||
>) and regularly report about the progress and my learnings on this
|
||||
exciting journey.
|
||||
|
@ -9,4 +9,6 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './how-do-i-get-my-finances-in-order-page.html'
|
||||
})
|
||||
export class HowDoIGetMyFinancesInOrderPageComponent {}
|
||||
export class HowDoIGetMyFinancesInOrderPageComponent {
|
||||
public routerLinkResources = ['/' + $localize`resources`];
|
||||
}
|
||||
|
@ -9,9 +9,9 @@
|
||||
<section class="mb-4">
|
||||
<p>
|
||||
Before you can think of
|
||||
<a [routerLink]="['/resources']">long-term investing</a>, you have
|
||||
to get your finances in order. Take a look at Peter's journey to see
|
||||
how you can achieve it, too.
|
||||
<a [routerLink]="routerLinkResources">long-term investing</a>, you
|
||||
have to get your finances in order. Take a look at Peter's journey
|
||||
to see how you can achieve it, too.
|
||||
</p>
|
||||
<p>
|
||||
Peter enjoys life, but sometimes he overspends a bit. He realizes it
|
||||
|
@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './500-stars-on-github-page.html'
|
||||
})
|
||||
export class FiveHundredStarsOnGitHubPageComponent {}
|
||||
export class FiveHundredStarsOnGitHubPageComponent {
|
||||
public routerLinkMarkets = ['/' + $localize`markets`];
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
}
|
||||
|
@ -71,10 +71,10 @@
|
||||
<h2 class="h4">Break-even Point</h2>
|
||||
<p>
|
||||
Despite the complicated
|
||||
<a [routerLink]="['/markets']">economic situation</a> at this time,
|
||||
the goal set at the beginning of the year to build a sustainable
|
||||
business and reach break-even with the SaaS offering (<a
|
||||
[routerLink]="['/pricing']"
|
||||
<a [routerLink]="routerLinkMarkets">economic situation</a> at this
|
||||
time, the goal set at the beginning of the year to build a
|
||||
sustainable business and reach break-even with the SaaS offering (<a
|
||||
[routerLink]="routerLinkPricing"
|
||||
>Ghostfolio Premium</a
|
||||
>) has been achieved. We will continue to leverage the revenue to
|
||||
further improve the fully managed cloud offering for our paying
|
||||
|
@ -11,5 +11,6 @@ import { GfPremiumIndicatorModule } from '@ghostfolio/ui/premium-indicator';
|
||||
templateUrl: './black-friday-2022-page.html'
|
||||
})
|
||||
export class BlackFriday2022PageComponent {
|
||||
public constructor() {}
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
}
|
||||
|
@ -35,17 +35,18 @@
|
||||
software presents the current assets (stocks, ETFs,
|
||||
cryptocurrencies, commodities etc.) in real time to make solid,
|
||||
data-driven investment decisions. Check out the numerous
|
||||
<a [routerLink]="['/features']">features</a> to manage your wealth.
|
||||
<a [routerLink]="routerLinkFeatures">features</a> to manage your
|
||||
wealth.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
<p>
|
||||
Snap the limited Black Friday 2022 deal before it’s gone. For
|
||||
detailed information on plans and pricing, please visit our
|
||||
<a [routerLink]="['/pricing']">pricing page</a>.
|
||||
<a [routerLink]="routerLinkPricing">pricing page</a>.
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<a color="primary" mat-flat-button [routerLink]="['/pricing']"
|
||||
<a color="primary" mat-flat-button [routerLink]="routerLinkPricing"
|
||||
>Get the Deal</a
|
||||
>
|
||||
</p>
|
||||
|
@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './1000-stars-on-github-page.html'
|
||||
})
|
||||
export class ThousandStarsOnGitHubPageComponent {}
|
||||
export class ThousandStarsOnGitHubPageComponent {
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
}
|
||||
|
@ -92,7 +92,7 @@
|
||||
<p>
|
||||
These self-hosting platforms allow users to run applications on
|
||||
their own hardware rather than rely on a
|
||||
<a [routerLink]="['/pricing']">SaaS offering</a>. As a result,
|
||||
<a [routerLink]="routerLinkPricing">SaaS offering</a>. As a result,
|
||||
Ghostfolio has become accessible to an even wider range of users who
|
||||
would like to take control of their wealth management.
|
||||
</p>
|
||||
@ -108,9 +108,9 @@
|
||||
As the project continues to evolve, we can expect to see even more
|
||||
exciting developments and innovations around Ghostfolio which guides
|
||||
users through the process of
|
||||
<a [routerLink]="['/features']">tracking their assets</a>, such as
|
||||
stocks, ETFs, or cryptocurrencies. Especially in the areas of data
|
||||
import and portfolio analysis.
|
||||
<a [routerLink]="routerLinkFeatures">tracking their assets</a>, such
|
||||
as stocks, ETFs, or cryptocurrencies. Especially in the areas of
|
||||
data import and portfolio analysis.
|
||||
</p>
|
||||
<p>
|
||||
We are honored to be a part of this vibrant and growing community,
|
||||
|
@ -9,4 +9,7 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './unlock-your-financial-potential-with-ghostfolio-page.html'
|
||||
})
|
||||
export class UnlockYourFinancialPotentialWithGhostfolioPageComponent {}
|
||||
export class UnlockYourFinancialPotentialWithGhostfolioPageComponent {
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResources = ['/' + $localize`resources`];
|
||||
}
|
||||
|
@ -44,13 +44,13 @@
|
||||
<h2 class="h4">Empowering Buy & Hold Strategies</h2>
|
||||
<p>
|
||||
For those committed to a
|
||||
<a [routerLink]="['/resources']">buy & hold strategy</a>, Ghostfolio
|
||||
provides an intuitive interface to monitor long-term investments.
|
||||
Users can track performance over time, gaining insights into
|
||||
portfolio growth and stability. With strong visualizations and
|
||||
reporting <a [routerLink]="['/features']">features</a>, Ghostfolio
|
||||
equips users to make well-informed decisions aligned with their
|
||||
long-term investment goals.
|
||||
<a [routerLink]="routerLinkResources">buy & hold strategy</a>,
|
||||
Ghostfolio provides an intuitive interface to monitor long-term
|
||||
investments. Users can track performance over time, gaining insights
|
||||
into portfolio growth and stability. With strong visualizations and
|
||||
reporting <a [routerLink]="routerLinkFeatures">features</a>,
|
||||
Ghostfolio equips users to make well-informed decisions aligned with
|
||||
their long-term investment goals.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
@ -91,7 +91,7 @@
|
||||
<h2 class="h4">Driving Financial Independence (FIRE)</h2>
|
||||
<p>
|
||||
Achieving
|
||||
<a [routerLink]="['/resources']">financial independence</a>
|
||||
<a [routerLink]="routerLinkResources">financial independence</a>
|
||||
including early retirement (<a
|
||||
href="../en/blog/2023/07/exploring-the-path-to-fire"
|
||||
>FIRE</a
|
||||
|
@ -9,4 +9,6 @@ import { RouterModule } from '@angular/router';
|
||||
standalone: true,
|
||||
templateUrl: './exploring-the-path-to-fire-page.html'
|
||||
})
|
||||
export class ExploringThePathToFirePageComponent {}
|
||||
export class ExploringThePathToFirePageComponent {
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
<div class="mb-3 text-muted"><small>2023-07-01</small></div>
|
||||
<img
|
||||
alt="Exploring the Path to Financial Independence and Retiring Early (FIRE) Teaser"
|
||||
class="border rounded w-100"
|
||||
class="rounded w-100"
|
||||
src="../assets/images/blog/20230701.jpg"
|
||||
title="Exploring the Path to Financial Independence and Retiring Early (FIRE)"
|
||||
/>
|
||||
@ -135,10 +135,10 @@
|
||||
track your investments, and make informed decisions to accelerate
|
||||
your progress towards financial independence. Ghostfolio also
|
||||
provides a dedicated
|
||||
<a [routerLink]="['/features']">FIRE calculator</a>, allowing you to
|
||||
simulate your customized plan to achieve FIRE. You get the tools to
|
||||
optimize your financial journey and confidently strive for a future
|
||||
that is both personally fulfilling and financially secure.
|
||||
<a [routerLink]="routerLinkFeatures">FIRE calculator</a>, allowing
|
||||
you to simulate your customized plan to achieve FIRE. You get the
|
||||
tools to optimize your financial journey and confidently strive for
|
||||
a future that is both personally fulfilling and financially secure.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4 py-3">
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
host: { class: 'page' },
|
||||
imports: [MatButtonModule, RouterModule],
|
||||
selector: 'gf-ghostfolio-joins-oss-friends-page',
|
||||
standalone: true,
|
||||
templateUrl: './ghostfolio-joins-oss-friends-page.html'
|
||||
})
|
||||
export class GhostfolioJoinsOssFriendsPageComponent {
|
||||
public routerLinkAboutOssFriends = ['/' + $localize`about`, 'oss-friends'];
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
<div class="blog container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<article>
|
||||
<div class="mb-4 text-center">
|
||||
<h1 class="mb-1">Ghostfolio joins OSS Friends</h1>
|
||||
<div class="mb-3 text-muted"><small>2023-08-23</small></div>
|
||||
<img
|
||||
alt="Ghostfolio joins OSS Friends Teaser"
|
||||
class="rounded w-100"
|
||||
src="../assets/images/blog/ghostfolio-joins-oss-friends.png"
|
||||
title="Ghostfolio joins OSS Friends"
|
||||
/>
|
||||
</div>
|
||||
<section class="mb-4">
|
||||
<p>
|
||||
We are excited to announce that Ghostfolio is now part of the
|
||||
<a [routerLink]="routerLinkAboutOssFriends">OSS Friends</a>. This
|
||||
new initiative is all about helping open source projects grow and
|
||||
become more popular.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
<h2 class="h4">The Story of OSS Friends</h2>
|
||||
<p>
|
||||
OSS Friends started as a simple
|
||||
<a
|
||||
href="https://twitter.com/formbricks/status/1660735970281508878"
|
||||
target="_blank"
|
||||
>post</a
|
||||
>
|
||||
on X (formerly known as <i>Twitter</i>). The idea came from
|
||||
<a href="https://formbricks.com" target="_blank">Formbricks</a>, an
|
||||
open source experience management platform to create surveys in
|
||||
minutes, and is all about giving open source projects a boost.
|
||||
</p>
|
||||
<p>
|
||||
If you are excited about the OSS Friends movement and want to bring
|
||||
your own open source project along, just take a moment to fill out
|
||||
<a
|
||||
href="https://app.formbricks.com/s/clhys1p9r001cpr0hu65rwh17"
|
||||
target="_blank"
|
||||
>this form</a
|
||||
>. Let’s work and learn together – all the open source way.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
<h2 class="h4">
|
||||
Ghostfolio – Next Generation Software for your Personal Finances
|
||||
</h2>
|
||||
<p>
|
||||
Money management can be tricky, especially when you have various
|
||||
investments like cryptocurrencies, ETFs and stocks in your
|
||||
portfolio. But guess what? There are cooler ways than staring at
|
||||
boring spreadsheets. Say hello to Ghostfolio, a privacy-first, open
|
||||
source dashboard for your personal finances.
|
||||
</p>
|
||||
</section>
|
||||
<section class="mb-4 py-3">
|
||||
<h2 class="h4 mb-0 text-center">
|
||||
Would you like to simplify asset tracking?
|
||||
</h2>
|
||||
<p class="lead mb-2 text-center">
|
||||
Ghostfolio empowers you to make informed investment decisions.
|
||||
</p>
|
||||
<div class="text-center">
|
||||
<a color="primary" href="https://ghostfol.io" mat-flat-button>
|
||||
Get Started
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="mb-4">
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Asset</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Collaboration</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Cryptocurrency</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Community</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Dashboard</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">ETF</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Finance</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Fintech</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Ghostfolio</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Initiative</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Innovation</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Investment</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Open Source</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">OSS</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">OSS Friends</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Personal Finance</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Platform</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Portfolio</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Privacy</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Software</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Stock</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Technology</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Tracking</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Wealth Management</span>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<span class="badge badge-light">Web3</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a i18n [routerLink]="['/blog']">Blog</a>
|
||||
</li>
|
||||
<li
|
||||
aria-current="page"
|
||||
class="active breadcrumb-item text-truncate"
|
||||
>
|
||||
Ghostfolio joins OSS Friends
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -136,6 +136,15 @@ const routes: Routes = [
|
||||
'./2023/07/exploring-the-path-to-fire/exploring-the-path-to-fire-page.component'
|
||||
).then((c) => c.ExploringThePathToFirePageComponent),
|
||||
title: 'Exploring the Path to FIRE'
|
||||
},
|
||||
{
|
||||
canActivate: [AuthGuard],
|
||||
path: '2023/08/ghostfolio-joins-oss-friends',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'./2023/08/ghostfolio-joins-oss-friends/ghostfolio-joins-oss-friends-page.component'
|
||||
).then((c) => c.GhostfolioJoinsOssFriendsPageComponent),
|
||||
title: 'Ghostfolio joins OSS Friends'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1,7 +1,39 @@
|
||||
<div class="container">
|
||||
<div class="mb-5 row">
|
||||
<div class="col">
|
||||
<h1 class="d-none d-sm-block h3 mb-4 text-center" i18n>Blog</h1>
|
||||
<h1 class="h3 mb-4 text-center">
|
||||
<span class="d-none d-sm-block" i18n>Blog</span>
|
||||
<small class="text-muted" i18n
|
||||
>Discover the latest Ghostfolio updates and insights on personal
|
||||
finance</small
|
||||
>
|
||||
</h1>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
<div class="flex-nowrap no-gutters row">
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
href="../en/blog/2023/08/ghostfolio-joins-oss-friends"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate">
|
||||
Ghostfolio joins OSS Friends
|
||||
</div>
|
||||
<div class="d-flex text-muted">2023-08-23</div>
|
||||
</div>
|
||||
<div class="align-items-center d-flex">
|
||||
<ion-icon
|
||||
class="chevron text-muted"
|
||||
name="chevron-forward-outline"
|
||||
size="small"
|
||||
></ion-icon>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="container p-0">
|
||||
|
@ -10,6 +10,10 @@ import { Subject, takeUntil } from 'rxjs';
|
||||
templateUrl: './faq-page.html'
|
||||
})
|
||||
export class FaqPageComponent implements OnDestroy {
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkMarkets = ['/' + $localize`markets`];
|
||||
public routerLinkPricing = ['/' + $localize`pricing`];
|
||||
public routerLinkRegister = ['/' + $localize`register`];
|
||||
public user: User;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
@ -39,7 +39,7 @@
|
||||
>
|
||||
<mat-card-content>
|
||||
Please find a feature overview to manage your wealth
|
||||
<a [routerLink]="['/features']">here</a>.
|
||||
<a [routerLink]="routerLinkFeatures">here</a>.
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
@ -47,7 +47,7 @@
|
||||
<mat-card-title>How do I start?</mat-card-title></mat-card-header
|
||||
>
|
||||
<mat-card-content>
|
||||
You can sign up via the “<a [routerLink]="['/register']"
|
||||
You can sign up via the “<a [routerLink]="routerLinkRegister"
|
||||
>Get Started</a
|
||||
>” button at the top of the page. You have multiple options to join
|
||||
Ghostfolio: Create an account with a security token, using
|
||||
@ -93,7 +93,7 @@
|
||||
world. The
|
||||
<a href="https://github.com/ghostfolio/ghostfolio">source code</a> is
|
||||
fully available as open source software (OSS). Thanks to our generous
|
||||
<a [routerLink]="['/pricing']">Ghostfolio Premium</a> users and
|
||||
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a> users and
|
||||
<a href="https://www.buymeacoffee.com/ghostfolio">sponsors</a> we have
|
||||
the ability to run a free, limited plan for novice
|
||||
investors.</mat-card-content
|
||||
@ -105,8 +105,8 @@
|
||||
>
|
||||
<mat-card-content
|
||||
>Yes, it is! Our
|
||||
<a [routerLink]="['/pricing']">pricing page</a> details everything you
|
||||
get for free.</mat-card-content
|
||||
<a [routerLink]="routerLinkPricing">pricing page</a> details
|
||||
everything you get for free.</mat-card-content
|
||||
>
|
||||
</mat-card>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
@ -127,7 +127,8 @@
|
||||
></mat-card-header
|
||||
>
|
||||
<mat-card-content
|
||||
>By offering <a [routerLink]="['/pricing']">Ghostfolio Premium</a>, a
|
||||
>By offering
|
||||
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>, a
|
||||
subscription plan with a managed hosting service and enhanced
|
||||
features, we fund our business while providing added value to our
|
||||
users.</mat-card-content
|
||||
@ -140,12 +141,12 @@
|
||||
></mat-card-header
|
||||
>
|
||||
<mat-card-content
|
||||
><a [routerLink]="['/pricing']">Ghostfolio Premium</a> is a fully
|
||||
><a [routerLink]="routerLinkPricing">Ghostfolio Premium</a> is a fully
|
||||
managed Ghostfolio cloud offering for ambitious investors. The revenue
|
||||
is used to cover the hosting infrastructure and to fund the ongoing
|
||||
development. It is the Open Source code base with some extras like the
|
||||
<a [routerLink]="['/markets']">markets overview</a> and a professional
|
||||
data provider.</mat-card-content
|
||||
<a [routerLink]="routerLinkMarkets">markets overview</a> and a
|
||||
professional data provider.</mat-card-content
|
||||
>
|
||||
</mat-card>
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
@ -156,9 +157,9 @@
|
||||
>
|
||||
<mat-card-content
|
||||
>Yes, you can try
|
||||
<a [routerLink]="['/pricing']">Ghostfolio Premium</a> by signing up
|
||||
for Ghostfolio and applying for a trial (see “My Ghostfolio”). It is
|
||||
easy, free and there is no commitment. You can stop using it at any
|
||||
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a> by signing
|
||||
up for Ghostfolio and applying for a trial (see “My Ghostfolio”). It
|
||||
is easy, free and there is no commitment. You can stop using it at any
|
||||
time.</mat-card-content
|
||||
>
|
||||
</mat-card>
|
||||
@ -214,8 +215,9 @@
|
||||
</mat-card-header>
|
||||
<mat-card-content
|
||||
>Any support for Ghostfolio is welcome. Be it with a
|
||||
<a [routerLink]="['/pricing']">Ghostfolio Premium</a> subscription to
|
||||
finance the hosting infrastructure, a positive rating in the
|
||||
<a [routerLink]="routerLinkPricing">Ghostfolio Premium</a>
|
||||
subscription to finance the hosting infrastructure, a positive rating
|
||||
in the
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=ch.dotsilver.ghostfolio.twa"
|
||||
>Google Play Store</a
|
||||
|
@ -1,10 +0,0 @@
|
||||
export const routes = [
|
||||
'faq',
|
||||
/////
|
||||
'domande-piu-frequenti',
|
||||
'foire-aux-questions',
|
||||
'haeufig-gestellte-fragen',
|
||||
'perguntas-mais-frequentes',
|
||||
'preguntas-mas-frecuentes',
|
||||
'vaak-gestelde-vragen'
|
||||
] as const;
|
@ -14,6 +14,7 @@ import { Subject, takeUntil } from 'rxjs';
|
||||
export class FeaturesPageComponent implements OnDestroy {
|
||||
public hasPermissionForSubscription: boolean;
|
||||
public info: InfoItem;
|
||||
public routerLinkRegister = ['/' + $localize`register`];
|
||||
public user: User;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
@ -211,7 +211,7 @@
|
||||
</h4>
|
||||
<p class="m-0">
|
||||
Check the current market mood (<a
|
||||
[routerLink]="['/resources']"
|
||||
[routerLink]="routerLinkResources"
|
||||
>Fear & Greed Index</a
|
||||
>) within the app.
|
||||
</p>
|
||||
@ -294,7 +294,7 @@
|
||||
</div>
|
||||
<div *ngIf="!user" class="row">
|
||||
<div class="col mt-3 text-center">
|
||||
<a color="primary" i18n mat-flat-button [routerLink]="['/register']"
|
||||
<a color="primary" i18n mat-flat-button [routerLink]="routerLinkRegister"
|
||||
>Get Started</a
|
||||
>
|
||||
</div>
|
||||
|
@ -1,8 +0,0 @@
|
||||
export const routes = [
|
||||
'features',
|
||||
/////
|
||||
'fonctionnalites',
|
||||
'funcionalidades',
|
||||
'funzionalita',
|
||||
'kenmerken'
|
||||
] as const;
|
@ -22,6 +22,8 @@ export class LandingPageComponent implements OnDestroy, OnInit {
|
||||
public hasPermissionForStatistics: boolean;
|
||||
public hasPermissionForSubscription: boolean;
|
||||
public hasPermissionToCreateUser: boolean;
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkRegister = ['/' + $localize`register`];
|
||||
public statistics: Statistics;
|
||||
public testimonials = [
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<h1 class="font-weight-bold intro mt-5">
|
||||
<h1 class="font-weight-bold intro mt-5" i18n>
|
||||
Manage your wealth like a boss
|
||||
</h1>
|
||||
<p class="lead mb-4">
|
||||
<p class="lead mb-4" i18n>
|
||||
Ghostfolio is a privacy-first, open source dashboard for your personal
|
||||
finances. Break down your asset allocation, know your net worth and make
|
||||
solid, data-driven investment decisions.
|
||||
@ -31,13 +31,20 @@
|
||||
<div class="button-container mb-5 row">
|
||||
<div class="align-items-center col d-flex justify-content-center">
|
||||
<ng-container *ngIf="hasPermissionToCreateUser">
|
||||
<a color="primary" mat-flat-button [routerLink]="['/register']">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-flat-button
|
||||
[routerLink]="routerLinkRegister"
|
||||
>
|
||||
Get Started
|
||||
</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="hasPermissionForDemo">
|
||||
<div *ngIf="hasPermissionToCreateUser" class="mx-3 text-muted">or</div>
|
||||
<a mat-stroked-button [routerLink]="['/demo']"> Live Demo </a>
|
||||
<div *ngIf="hasPermissionToCreateUser" class="mx-3 text-muted" i18n>
|
||||
or
|
||||
</div>
|
||||
<a i18n mat-stroked-button [routerLink]="['/demo']">Live Demo</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
@ -53,6 +60,7 @@
|
||||
[routerLink]="['/open']"
|
||||
>
|
||||
<gf-value
|
||||
i18n
|
||||
icon="people-outline"
|
||||
size="large"
|
||||
[value]="statistics?.activeUsers30d ?? '-'"
|
||||
@ -70,6 +78,7 @@
|
||||
[routerLink]="['/open']"
|
||||
>
|
||||
<gf-value
|
||||
i18n
|
||||
icon="star-outline"
|
||||
size="large"
|
||||
[value]="statistics?.gitHubStargazers ?? '-'"
|
||||
@ -87,6 +96,7 @@
|
||||
[routerLink]="['/open']"
|
||||
>
|
||||
<gf-value
|
||||
i18n
|
||||
icon="cloud-download-outline"
|
||||
size="large"
|
||||
[value]="statistics?.dockerHubPulls ?? '-'"
|
||||
@ -97,7 +107,9 @@
|
||||
</div>
|
||||
|
||||
<div class="row mb-5">
|
||||
<div class="col-12 text-center text-muted"><small>As seen in</small></div>
|
||||
<div class="col-12 text-center text-muted">
|
||||
<small i18n>As seen in</small>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex justify-content-center my-1">
|
||||
<a
|
||||
class="d-block logo logo-alternative-to mask"
|
||||
@ -190,11 +202,11 @@
|
||||
|
||||
<div class="pt-3 row">
|
||||
<div class="col text-center">
|
||||
<h2 class="h4 mb-1 text-center">
|
||||
<h2 class="h4 mb-1 text-center" i18n>
|
||||
Protect your <strong>assets</strong>. Refine your
|
||||
<strong>personal investment strategy</strong>.
|
||||
</h2>
|
||||
<p class="lead m-0">
|
||||
<p class="lead m-0" i18n>
|
||||
Ghostfolio empowers busy people to keep track of stocks, ETFs or
|
||||
cryptocurrencies without being tracked.
|
||||
</p>
|
||||
@ -205,19 +217,20 @@
|
||||
<div class="col-md-4 my-2">
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-header>
|
||||
<mat-card-title>360° View</mat-card-title>
|
||||
<mat-card-title i18n>360° View</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-card-content i18n>
|
||||
Get the full picture of your personal finances across multiple
|
||||
platforms.
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div class="col-md-4 my-2">
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Web3 Ready</mat-card-title>
|
||||
<mat-card-title i18n>Web3 Ready</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-card-content i18n>
|
||||
Use Ghostfolio anonymously and own your financial data.
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@ -225,9 +238,9 @@
|
||||
<div class="col-md-4 my-2">
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Open Source</mat-card-title>
|
||||
<mat-card-title i18n>Open Source</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-card-content i18n>
|
||||
Benefit from continuous improvements through a strong community.
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@ -236,51 +249,53 @@
|
||||
|
||||
<div class="row my-5">
|
||||
<div class="col-md-6 offset-md-3">
|
||||
<h2 class="h4 mb-1 text-center">Why <strong>Ghostfolio</strong>?</h2>
|
||||
<p class="lead mb-3 text-center">Ghostfolio is for you if you are...</p>
|
||||
<h2 class="h4 mb-1 text-center" i18n>Why <strong>Ghostfolio</strong>?</h2>
|
||||
<p class="lead mb-3 text-center" i18n>
|
||||
Ghostfolio is for you if you are...
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">💼 </span
|
||||
><span
|
||||
><span i18n
|
||||
>trading stocks, ETFs or cryptocurrencies on multiple
|
||||
platforms</span
|
||||
>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">🏦</span
|
||||
><span>pursuing a buy & hold strategy</span>
|
||||
><span i18n>pursuing a buy & hold strategy</span>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">🎯</span
|
||||
><span
|
||||
><span i18n
|
||||
>interested in getting insights of your portfolio composition</span
|
||||
>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">👻</span
|
||||
><span>valuing privacy and data ownership</span>
|
||||
><span i18n>valuing privacy and data ownership</span>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">🧘</span><span>into minimalism</span>
|
||||
<span class="mr-3">🧘</span><span i18n>into minimalism</span>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">🧺</span
|
||||
><span>caring about diversifying your financial resources</span>
|
||||
><span i18n>caring about diversifying your financial resources</span>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">🆓</span
|
||||
><span>interested in financial independence</span>
|
||||
><span i18n>interested in financial independence</span>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">🙅</span
|
||||
><span>saying no to spreadsheets in {{ currentYear }}</span>
|
||||
><span i18n>saying no to spreadsheets in {{ currentYear }}</span>
|
||||
</li>
|
||||
<li class="d-flex mb-3">
|
||||
<span class="mr-3">😎</span><span>still reading this list</span>
|
||||
<span class="mr-3">😎</span><span i18n>still reading this list</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mt-4 text-center">
|
||||
<a mat-stroked-button [routerLink]="['/about']"
|
||||
<a i18n mat-stroked-button [routerLink]="routerLinkAbout"
|
||||
>Learn more about Ghostfolio</a
|
||||
>
|
||||
</div>
|
||||
@ -289,7 +304,7 @@
|
||||
|
||||
<div class="row my-5">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 mb-1 text-center">
|
||||
<h2 class="h4 mb-1 text-center" i18n>
|
||||
What our <strong>users</strong> are saying
|
||||
</h2>
|
||||
</div>
|
||||
@ -319,7 +334,7 @@
|
||||
|
||||
<div *ngIf="hasPermissionForSubscription" class="row my-5">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 text-center">
|
||||
<h2 class="h4 text-center" i18n>
|
||||
Members from around the globe are using
|
||||
<a href="pricing"><strong>Ghostfolio Premium</strong></a>
|
||||
</h2>
|
||||
@ -334,17 +349,17 @@
|
||||
|
||||
<div *ngIf="hasPermissionForSubscription" class="row my-3">
|
||||
<div class="col-12">
|
||||
<h2 class="h4 mb-1 text-center">
|
||||
<h2 class="h4 mb-1 text-center" i18n>
|
||||
How does <strong>Ghostfolio</strong> work?
|
||||
</h2>
|
||||
<p class="lead mb-3 text-center">Get started in only 3 steps</p>
|
||||
<p class="lead mb-3 text-center" i18n>Get started in only 3 steps</p>
|
||||
</div>
|
||||
<div class="col-md-4 my-2">
|
||||
<mat-card appearance="outlined" class="h-100">
|
||||
<mat-card-content class="d-flex flex-row">
|
||||
<div class="flex-grow-1">
|
||||
<div class="font-weight-bold">Sign up anonymously*</div>
|
||||
<div class="text-muted">
|
||||
<div class="font-weight-bold" i18n>Sign up anonymously*</div>
|
||||
<div class="text-muted" i18n>
|
||||
<small>* no e-mail address nor credit card required</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -356,7 +371,7 @@
|
||||
<mat-card appearance="outlined" class="h-100">
|
||||
<mat-card-content class="d-flex flex-row">
|
||||
<div class="flex-grow-1">
|
||||
<div class="font-weight-bold">
|
||||
<div class="font-weight-bold" i18n>
|
||||
Add any of your historical transactions
|
||||
</div>
|
||||
</div>
|
||||
@ -368,7 +383,7 @@
|
||||
<mat-card appearance="outlined" class="h-100">
|
||||
<mat-card-content class="d-flex flex-row">
|
||||
<div class="flex-grow-1">
|
||||
<div class="font-weight-bold">
|
||||
<div class="font-weight-bold" i18n>
|
||||
Get valuable insights of your portfolio composition
|
||||
</div>
|
||||
</div>
|
||||
@ -380,19 +395,24 @@
|
||||
|
||||
<div *ngIf="hasPermissionToCreateUser" class="row my-5">
|
||||
<div class="col">
|
||||
<h2 class="h4 mb-1 text-center">Are <strong>you</strong> ready?</h2>
|
||||
<p class="lead mb-3 text-center">
|
||||
<h2 class="h4 mb-1 text-center" i18n>Are <strong>you</strong> ready?</h2>
|
||||
<p class="lead mb-3 text-center" i18n>
|
||||
Join now<ng-container *ngIf="hasPermissionForDemo">
|
||||
or check out the example account</ng-container
|
||||
>
|
||||
</p>
|
||||
<div class="align-items-center d-flex justify-content-center py-2">
|
||||
<a color="primary" mat-flat-button [routerLink]="['/register']">
|
||||
<a
|
||||
color="primary"
|
||||
i18n
|
||||
mat-flat-button
|
||||
[routerLink]="routerLinkRegister"
|
||||
>
|
||||
Get Started
|
||||
</a>
|
||||
<ng-container *ngIf="hasPermissionForDemo">
|
||||
<div class="mx-3 text-muted">or</div>
|
||||
<a mat-stroked-button [routerLink]="['/demo']"> Live Demo </a>
|
||||
<div class="mx-3 text-muted" i18n>or</div>
|
||||
<a i18n mat-stroked-button [routerLink]="['/demo']">Live Demo</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +0,0 @@
|
||||
export const routes = [
|
||||
'markets',
|
||||
/////
|
||||
'maerkte',
|
||||
'marches',
|
||||
'markten',
|
||||
'mercados',
|
||||
'mercati'
|
||||
] as const;
|
@ -260,30 +260,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-header class="overflow-hidden w-100">
|
||||
<mat-card-title class="align-items-center d-flex text-truncate"
|
||||
><span i18n>By Country</span
|
||||
><gf-premium-indicator
|
||||
*ngIf="user?.subscription?.type === 'Basic'"
|
||||
class="ml-1"
|
||||
></gf-premium-indicator
|
||||
></mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[colorScheme]="user?.settings?.colorScheme"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
></gf-portfolio-proportion-chart>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-header class="overflow-hidden w-100">
|
||||
@ -326,5 +302,29 @@
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<mat-card appearance="outlined" class="mb-3">
|
||||
<mat-card-header class="overflow-hidden w-100">
|
||||
<mat-card-title class="align-items-center d-flex text-truncate"
|
||||
><span i18n>By Country</span
|
||||
><gf-premium-indicator
|
||||
*ngIf="user?.subscription?.type === 'Basic'"
|
||||
class="ml-1"
|
||||
></gf-premium-indicator
|
||||
></mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<gf-portfolio-proportion-chart
|
||||
[baseCurrency]="user?.settings?.baseCurrency"
|
||||
[colorScheme]="user?.settings?.colorScheme"
|
||||
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
|
||||
[keys]="['name']"
|
||||
[locale]="user?.settings?.locale"
|
||||
[maxItems]="10"
|
||||
[positions]="countries"
|
||||
></gf-portfolio-proportion-chart>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,6 +29,8 @@ export class PricingPageComponent implements OnDestroy, OnInit {
|
||||
public isLoggedIn: boolean;
|
||||
public price: number;
|
||||
public priceId: string;
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkRegister = ['/' + $localize`register`];
|
||||
public user: User;
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
@ -21,7 +21,7 @@
|
||||
<a href="mailto:hi@ghostfol.io?Subject=Student Discount">here</a> with
|
||||
your university e-mail address.
|
||||
</p>
|
||||
<p>
|
||||
<p i18n>
|
||||
If you prefer to run Ghostfolio on your own infrastructure, please
|
||||
find the source code and further instructions on
|
||||
<a href="https://github.com/ghostfolio/ghostfolio">GitHub</a>.
|
||||
@ -106,7 +106,7 @@
|
||||
class="mr-1"
|
||||
name="checkmark-circle-outline"
|
||||
></ion-icon>
|
||||
<a i18n [routerLink]="['/features']"
|
||||
<a i18n [routerLink]="routerLinkFeatures"
|
||||
>and more Features...</a
|
||||
>
|
||||
</li>
|
||||
@ -302,7 +302,7 @@
|
||||
class="mr-1"
|
||||
name="checkmark-circle-outline"
|
||||
></ion-icon>
|
||||
<a i18n [routerLink]="['/features']"
|
||||
<a i18n [routerLink]="routerLinkFeatures"
|
||||
>and more Features...</a
|
||||
>
|
||||
</li>
|
||||
@ -360,7 +360,7 @@
|
||||
</div>
|
||||
<div *ngIf="!user" class="row">
|
||||
<div class="col mt-3 text-center">
|
||||
<a color="primary" i18n mat-flat-button [routerLink]="['/register']">
|
||||
<a color="primary" i18n mat-flat-button [routerLink]="routerLinkRegister">
|
||||
Get Started
|
||||
</a>
|
||||
<p class="m-0 text-muted"><small i18n>It’s free.</small></p>
|
||||
|
@ -1,10 +0,0 @@
|
||||
export const routes = [
|
||||
'pricing',
|
||||
/////
|
||||
'precios',
|
||||
'precos',
|
||||
'preise',
|
||||
'prezzi',
|
||||
'prijzen',
|
||||
'prix'
|
||||
] as const;
|
@ -1,10 +0,0 @@
|
||||
export const routes = [
|
||||
'register',
|
||||
/////
|
||||
'enregistrement',
|
||||
'iscrizione',
|
||||
'registo',
|
||||
'registratie',
|
||||
'registrierung',
|
||||
'registro'
|
||||
] as const;
|
@ -22,7 +22,7 @@ const routes: Routes = [
|
||||
path: `open-source-alternative-to-${key}`,
|
||||
loadComponent: () =>
|
||||
import(`./products/${key}-page.component`).then(() => component),
|
||||
title: `Open Source Alternative to ${name}`
|
||||
title: $localize`Open Source Alternative to ${name}`
|
||||
};
|
||||
})
|
||||
];
|
||||
|
@ -10,9 +10,11 @@ import { products } from './products';
|
||||
templateUrl: './personal-finance-tools-page.html'
|
||||
})
|
||||
export class PersonalFinanceToolsPageComponent implements OnDestroy {
|
||||
public pathResources = '/' + $localize`resources`;
|
||||
public products = products.filter(({ key }) => {
|
||||
return key !== 'ghostfolio';
|
||||
});
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
|
@ -5,15 +5,15 @@
|
||||
Discover Open Source Alternatives for Personal Finance Tools
|
||||
</h1>
|
||||
<div class="introduction mb-4">
|
||||
<p>
|
||||
<p i18n>
|
||||
This overview page features a curated collection of personal finance
|
||||
tools compared to the open source alternative
|
||||
<a [routerLink]="['/about']">Ghostfolio</a>. If you value
|
||||
<a [routerLink]="routerLinkAbout">Ghostfolio</a>. If you value
|
||||
transparency, data privacy, and community collaboration, Ghostfolio
|
||||
provides an excellent opportunity to take control of your financial
|
||||
management.
|
||||
</p>
|
||||
<p>
|
||||
<p i18n>
|
||||
Explore the links below to compare a variety of personal finance tools
|
||||
with Ghostfolio.
|
||||
</p>
|
||||
@ -29,10 +29,10 @@
|
||||
<a
|
||||
class="d-flex overflow-hidden w-100"
|
||||
title="Compare Ghostfolio to {{ product.name }}"
|
||||
[routerLink]="['/resources', 'personal-finance-tools', 'open-source-alternative-to-' + product.key]"
|
||||
[routerLink]="[pathResources, 'personal-finance-tools', 'open-source-alternative-to-' + product.key]"
|
||||
>
|
||||
<div class="flex-grow-1 overflow-hidden">
|
||||
<div class="h6 m-0 text-truncate">
|
||||
<div class="h6 m-0 text-truncate" i18n>
|
||||
Open Source Alternative to {{ product.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,19 +4,22 @@
|
||||
<article>
|
||||
<div class="mb-4 text-center">
|
||||
<h1 class="mb-1">
|
||||
<strong>Ghostfolio</strong>: The Open Source Alternative to
|
||||
<strong>{{ product2.name }}</strong>
|
||||
<strong>Ghostfolio</strong>:
|
||||
<ng-container i18n>The Open Source Alternative to</ng-container
|
||||
> <strong>{{ product2.name }}</strong>
|
||||
</h1>
|
||||
</div>
|
||||
<section class="mb-4">
|
||||
<p>
|
||||
Are you looking for an open source alternative to {{ product2.name
|
||||
}}? <a [routerLink]="['/about']">Ghostfolio</a> is a powerful
|
||||
}}? <a [routerLink]="routerLinkAbout">Ghostfolio</a> is a powerful
|
||||
portfolio management tool that provides individuals with a
|
||||
comprehensive platform to track, analyze, and optimize their
|
||||
investments. Whether you are an experienced investor or just
|
||||
starting out, Ghostfolio offers an intuitive user interface and a
|
||||
<a [routerLink]="['/features']">wide range of functionalities</a>
|
||||
<a [routerLink]="routerLinkFeatures"
|
||||
>wide range of functionalities</a
|
||||
>
|
||||
to help you make informed decisions and take control of your
|
||||
financial future.
|
||||
</p>
|
||||
@ -280,7 +283,7 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a i18n [routerLink]="['/resources', 'personal-finance-tools']"
|
||||
<a i18n [routerLink]="routerLinkResourcesPersonalFinanceTools"
|
||||
>Personal Finance Tools</a
|
||||
>
|
||||
</li>
|
||||
|
@ -21,4 +21,11 @@ export class AltooPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'altoo';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class CopilotMoneyPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'copilot-money';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class DeltaPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'delta';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class DivvyDiaryPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'divvydiary';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class ExirioPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'exirio';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class FolisharePageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'folishare';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class GetquinPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'getquin';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class GoSpatzPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'gospatz';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class JustEtfPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'justetf';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class KuberaPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'kubera';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class MarketsShPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'markets.sh';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class MaybeFinancePageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'maybe-finance';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class MonsePageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'monse';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class ParqetPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'parqet';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class PlannixPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'plannix';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class PortfolioDividendTrackerPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'portfolio-dividend-tracker';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class PortseidoPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'portseido';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class ProjectionLabPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'projectionlab';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class SeekingAlphaPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'seeking-alpha';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
@ -21,4 +21,11 @@ export class SharesightPageComponent {
|
||||
public product2 = products.find(({ key }) => {
|
||||
return key === 'sharesight';
|
||||
});
|
||||
|
||||
public routerLinkAbout = ['/' + $localize`about`];
|
||||
public routerLinkFeatures = ['/' + $localize`features`];
|
||||
public routerLinkResourcesPersonalFinanceTools = [
|
||||
'/' + $localize`resources`,
|
||||
'personal-finance-tools'
|
||||
];
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user