mirror of
https://github.com/romancm/gamebrary
synced 2024-11-10 05:34:15 +00:00
Lots of cool new stuff
This commit is contained in:
parent
c4f662da66
commit
439916cf30
41 changed files with 2104 additions and 622 deletions
|
@ -1,5 +1,4 @@
|
|||
// firebase emulators:start --only functions
|
||||
// TODO: Inject token using axios middleware
|
||||
// Add json object in .runtimeconfig.json to use env variables locally
|
||||
|
||||
const functions = require('firebase-functions');
|
||||
|
|
506
google-custom-search-response.json
Normal file
506
google-custom-search-response.json
Normal file
|
@ -0,0 +1,506 @@
|
|||
{
|
||||
"kind": "customsearch#search",
|
||||
"url": {
|
||||
"type": "application/json",
|
||||
"template": "https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&relatedSite={relatedSite?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
|
||||
},
|
||||
"queries": {
|
||||
"request": [
|
||||
{
|
||||
"title": "Google Custom Search - Most Watched Games on Twitch twitchtracker",
|
||||
"totalResults": "227000",
|
||||
"searchTerms": "Most Watched Games on Twitch twitchtracker",
|
||||
"count": 10,
|
||||
"startIndex": 1,
|
||||
"inputEncoding": "utf8",
|
||||
"outputEncoding": "utf8",
|
||||
"safe": "off",
|
||||
"cx": "25c3c6c97e0db4365"
|
||||
}
|
||||
],
|
||||
"nextPage": [
|
||||
{
|
||||
"title": "Google Custom Search - Most Watched Games on Twitch twitchtracker",
|
||||
"totalResults": "227000",
|
||||
"searchTerms": "Most Watched Games on Twitch twitchtracker",
|
||||
"count": 10,
|
||||
"startIndex": 11,
|
||||
"inputEncoding": "utf8",
|
||||
"outputEncoding": "utf8",
|
||||
"safe": "off",
|
||||
"cx": "25c3c6c97e0db4365"
|
||||
}
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"title": "gamebrary"
|
||||
},
|
||||
"searchInformation": {
|
||||
"searchTime": 0.515786,
|
||||
"formattedSearchTime": "0.52",
|
||||
"totalResults": "227000",
|
||||
"formattedTotalResults": "227,000"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Most Watched Games on Twitch, October 2022 · TwitchTracker",
|
||||
"htmlTitle": "<b>Most Watched Games on Twitch</b>, October 2022 · <b>TwitchTracker</b>",
|
||||
"link": "https://twitchtracker.com/games",
|
||||
"displayLink": "twitchtracker.com",
|
||||
"snippet": "Most Watched Games on Twitch ; #1. Just Chatting. 315K ; #2. Overwatch 2. 273K ; #3. League of Legends. 218K ; #4. Grand Theft Auto V · 113K ; #5. Counter-Strike: ...",
|
||||
"htmlSnippet": "<b>Most Watched Games on Twitch</b> ; #1. Just Chatting. 315K ; #2. Overwatch 2. 273K ; #3. League of Legends. 218K ; #4. Grand Theft Auto V · 113K ; #5. Counter-Strike: ...",
|
||||
"cacheId": "Txl-67EDxH0J",
|
||||
"formattedUrl": "https://twitchtracker.com/games",
|
||||
"htmlFormattedUrl": "https://<b>twitchtracker</b>.com/<b>games</b>",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSEvgEyyzCMnZfi_KDj9q7HcADBRt6eZgPo5_M78JgoaQsngLtPtUuJNy7D",
|
||||
"width": "275",
|
||||
"height": "183"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"og:image": "https://twitchtracker.com/img/share-tt.png",
|
||||
"theme-color": "#ffffff",
|
||||
"twitter:card": "summary_large_image",
|
||||
"og:type": "website",
|
||||
"twitter:site": "@twitchtracker_",
|
||||
"og:site_name": "TwitchTracker",
|
||||
"viewport": "width=device-width, initial-scale=1.0",
|
||||
"og:title": "Most Watched Games on Twitch, October 2022",
|
||||
"og:locale": "en_US",
|
||||
"og:url": "https://twitchtracker.com/games",
|
||||
"og:description": "Ranked by average concurrent viewers for the last 7 days, October 2022"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://twitchtracker.com/img/share-tt.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Most Watched Games on Twitch | Esports Content and Total",
|
||||
"htmlTitle": "<b>Most Watched Games on Twitch</b> | Esports Content and Total",
|
||||
"link": "https://newzoo.com/insights/rankings/top-games-twitch",
|
||||
"displayLink": "newzoo.com",
|
||||
"snippet": "Explore the ranking below to find the most popular games on Twitch across the top channels that broadcast game-related content. Each game's position in the ...",
|
||||
"htmlSnippet": "Explore the ranking below to find the <b>most popular games on Twitch</b> across the top channels that broadcast game-related content. Each game's position in the ...",
|
||||
"cacheId": "pw0bWHtq5yEJ",
|
||||
"formattedUrl": "https://newzoo.com/insights/rankings/top-games-twitch",
|
||||
"htmlFormattedUrl": "https://newzoo.com/insights/rankings/<b>top</b>-<b>games</b>-<b>twitch</b>",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSQSOKzf4Z2TsxKZ7nBT2w32mQueu4DtyWf3CGfG0Gnl78MukQnu_hmlVM",
|
||||
"width": "300",
|
||||
"height": "168"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"og:image": "https://newzoo.com/wp-content/uploads/2022/02/Rankings_MostWatchedonTwitch.jpg",
|
||||
"og:type": "article",
|
||||
"og:image:width": "1920",
|
||||
"twitter:card": "summary",
|
||||
"og:site_name": "Newzoo",
|
||||
"og:title": "Most Watched Games on Twitch | Esports Content and Total",
|
||||
"og:image:height": "1080",
|
||||
"twitter:label1": "Est. reading time",
|
||||
"og:image:type": "image/jpeg",
|
||||
"og:description": "Most viewed games on Twitch every month, including the watched esports content and overall content. Click to see the ranking!",
|
||||
"article:publisher": "https://www.facebook.com/NewzooHQ/",
|
||||
"twitter:data1": "1 minute",
|
||||
"facebook-domain-verification": "5azu56bev83xm26g6x5d8s0xe1kkxy",
|
||||
"twitter:site": "@NewzooHQ",
|
||||
"article:modified_time": "2022-05-20T08:11:21+00:00",
|
||||
"viewport": "width=device-width, initial-scale=1.0",
|
||||
"og:locale": "en_US",
|
||||
"og:url": "https://newzoo.com/insights/rankings/top-games-twitch"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://newzoo.com/wp-content/uploads/2022/02/Rankings_MostWatchedonTwitch.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Twitch Usage and Growth Statistics: How Many People Use Twitch ...",
|
||||
"htmlTitle": "<b>Twitch</b> Usage and Growth Statistics: How Many People Use <b>Twitch</b> ...",
|
||||
"link": "https://backlinko.com/twitch-users",
|
||||
"displayLink": "backlinko.com",
|
||||
"snippet": "Jan 5, 2022 ... Most popular Twitch channels; Most popular Twitch games; Twitch by the money. Let's get right into it: Twitch statistics (Top Picks). Twitch ...",
|
||||
"htmlSnippet": "Jan 5, 2022 <b>...</b> <b>Most popular Twitch</b> channels; <b>Most popular Twitch games</b>; <b>Twitch</b> by the money. Let's get right into it: <b>Twitch</b> statistics (<b>Top</b> Picks). <b>Twitch</b> ...",
|
||||
"cacheId": "Id-xSK1XCEcJ",
|
||||
"formattedUrl": "https://backlinko.com/twitch-users",
|
||||
"htmlFormattedUrl": "https://backlinko.com/<b>twitch</b>-users",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSm9HjdDrDlDhMyWycw_5H0IzujUND6QHoZSuIeZBlFurrdk0PXpBOz31I",
|
||||
"width": "311",
|
||||
"height": "162"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"application-name": "Backlinko",
|
||||
"msapplication-tilecolor": "#2b5797",
|
||||
"og:image": "https://api.backlinko.com/app/uploads/2018/07/twitch-users-post-banner.png",
|
||||
"theme-color": "#ffffff",
|
||||
"og:type": "article",
|
||||
"og:image:width": "1600",
|
||||
"article:published_time": "2021-01-26T09:15:48-05:00",
|
||||
"twitter:card": "summary_large_image",
|
||||
"og:site_name": "Backlinko",
|
||||
"apple-mobile-web-app-title": "Backlinko",
|
||||
"og:title": "Twitch Usage and Growth Statistics: How Many People Use Twitch in 2022?",
|
||||
"og:image:height": "837",
|
||||
"bingbot": "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1",
|
||||
"og:description": "In-depth look at the user base of the popular video streaming platform, Twitch.",
|
||||
"twitter:creator": "@Backlinko",
|
||||
"article:publisher": "https://www.facebook.com/Backlinko",
|
||||
"twitter:image": "https://api.backlinko.com/app/uploads/2018/07/twitch-users-post-banner.png",
|
||||
"next-head-count": "36",
|
||||
"twitter:site": "@Backlinko",
|
||||
"article:modified_time": "2022-01-05T10:52:02+00:00",
|
||||
"viewport": "width=device-width, initial-scale=1, shrink-to-fit=no",
|
||||
"og:locale": "en_US",
|
||||
"og:url": "https://backlinko.com/twitch-users"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://api.backlinko.com/app/uploads/2018/07/twitch-users-post-banner.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "10 years of Twitch: Looking back on the top games | Nerd Street",
|
||||
"htmlTitle": "10 years of <b>Twitch</b>: Looking back on the <b>top games</b> | Nerd Street",
|
||||
"link": "https://nerdstreet.com/news/2021/6/twitch-10-year-anniversary-top-games-by-year",
|
||||
"displayLink": "nerdstreet.com",
|
||||
"snippet": "Jun 7, 2021 ... Emmett Shear, who would go on to become the CEO, that's what he was watching the most.” In June 2021, Starcraft 2 was the 57th most watched game ...",
|
||||
"htmlSnippet": "Jun 7, 2021 <b>...</b> Emmett Shear, who would go on to become the CEO, that's what he was <b>watching</b> the <b>most</b>.” In June 2021, Starcraft 2 was the 57th <b>most watched game</b> ...",
|
||||
"cacheId": "3-WPbSYXJjYJ",
|
||||
"formattedUrl": "https://nerdstreet.com/news/.../twitch-10-year-anniversary-top-games-by-year",
|
||||
"htmlFormattedUrl": "https://nerdstreet.com/news/.../<b>twitch</b>-10-year-anniversary-<b>top</b>-<b>games</b>-by-year",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRuBdjA9GdF1iLdweUs8D7KOrC_Uj_Xf6OgkdlbM7wUgnOdzFekkSsEqjw",
|
||||
"width": "300",
|
||||
"height": "168"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"msapplication-tilecolor": "#da532c",
|
||||
"og:image": "https://cdn.sanity.io/images/zoz4y99f/production/c1889cfcfd99d10cd31e148119c23983e806db44-1600x900.png",
|
||||
"theme-color": "#000000",
|
||||
"twitter:title": "10 years of Twitch: Looking back on the top games | Nerd Street",
|
||||
"og:type": "article",
|
||||
"twitter:card": "summary_large_image",
|
||||
"og:site_name": "Nerd Street",
|
||||
"og:title": "10 years of Twitch: Looking back on the top games",
|
||||
"og:article:publish_time": "2021-06-07T16:19:56Z",
|
||||
"og:description": "From being the go-to site for League of Legends esports to seeing the rise of the battle royale genre, here’s a look back on the top games throughout Twitch’s first decade.",
|
||||
"twitter:creator": "@Mitch_Reames",
|
||||
"next-head-count": "25",
|
||||
"twitter:site": "@nerdstgamers",
|
||||
"viewport": "initial-scale=1.0, width=device-width",
|
||||
"og:url": "https://nerdstreet.com/news/2021/6/twitch-10-year-anniversary-top-games-by-year",
|
||||
"og:article:author": "Mitch Reames"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://cdn.sanity.io/images/zoz4y99f/production/c1889cfcfd99d10cd31e148119c23983e806db44-1600x900.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "SullyGnome: Twitch Stats and Analytics - Games & Channels",
|
||||
"htmlTitle": "SullyGnome: <b>Twitch</b> Stats and Analytics - <b>Games</b> & Channels",
|
||||
"link": "https://sullygnome.com/",
|
||||
"displayLink": "sullygnome.com",
|
||||
"snippet": "Twitch stats and analytics for your favorite streamers and games. View the fastest growing channels and most popular games.",
|
||||
"htmlSnippet": "<b>Twitch</b> stats and analytics for your favorite streamers and <b>games</b>. View the fastest growing channels and <b>most popular games</b>.",
|
||||
"cacheId": "7_sU_Z2qmOcJ",
|
||||
"formattedUrl": "https://sullygnome.com/",
|
||||
"htmlFormattedUrl": "https://sullygnome.com/",
|
||||
"pagemap": {
|
||||
"metatags": [
|
||||
{
|
||||
"viewport": "width=device-width, initial-scale=1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "50+ Twitch Statistics for Content Creators in 2022 | Uscreen",
|
||||
"htmlTitle": "50+ <b>Twitch</b> Statistics for Content Creators in 2022 | Uscreen",
|
||||
"link": "https://www.uscreen.tv/blog/twitch-statistics/",
|
||||
"displayLink": "www.uscreen.tv",
|
||||
"snippet": "Jun 30, 2022 ... (TwitchTracker); In 2022, people have already watched over 6 ... The most popular game played on Twitch live streams is League of Legends.",
|
||||
"htmlSnippet": "Jun 30, 2022 <b>...</b> (<b>TwitchTracker</b>); In 2022, people have already <b>watched</b> over 6 ... The <b>most popular game</b> played on <b>Twitch</b> live streams is League of Legends.",
|
||||
"cacheId": "0QtmwNPFIxkJ",
|
||||
"formattedUrl": "https://www.uscreen.tv/blog/twitch-statistics/",
|
||||
"htmlFormattedUrl": "https://www.uscreen.tv/blog/<b>twitch</b>-statistics/",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSs_ox-gFivRoK_sE6P214g2zG9Dtdt4f7BmWie8XS_gup5PruD_TEQ77E",
|
||||
"width": "297",
|
||||
"height": "170"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"p:domain_verify": "53aef21ad69a7ee8ba0300f0f20a28a1",
|
||||
"og:image": "https://www.uscreen.tv/wp-content/uploads/2022/06/twitch-statistics-hero.jpg",
|
||||
"og:type": "article",
|
||||
"article:published_time": "2022-06-30T09:41:19+00:00",
|
||||
"og:image:width": "1050",
|
||||
"twitter:card": "summary_large_image",
|
||||
"og:site_name": "Uscreen",
|
||||
"og:title": "50+ Twitch Statistics for Content Creators in 2022",
|
||||
"og:image:height": "600",
|
||||
"twitter:label1": "Written by",
|
||||
"twitter:label2": "Est. reading time",
|
||||
"og:image:type": "image/jpeg",
|
||||
"msapplication-tileimage": "https://www.uscreen.tv/wp-content/uploads/2018/04/favico.png",
|
||||
"og:description": "Pore over 50+ Twitch statistics you should know as a content creator in 2022.",
|
||||
"twitter:creator": "@uscreentv",
|
||||
"article:publisher": "https://www.facebook.com/uscreentv/",
|
||||
"twitter:data1": "Parris Johnson",
|
||||
"twitter:data2": "10 minutes",
|
||||
"ahrefs-site-verification": "eca3e5350dd3e828ec60185a42d51d53976ff03dd3582abda3d2616f5d520995",
|
||||
"twitter:site": "@uscreentv",
|
||||
"article:modified_time": "2022-10-04T07:35:32+00:00",
|
||||
"viewport": "width=device-width, initial-scale=1",
|
||||
"og:locale": "en_US",
|
||||
"og:url": "https://www.uscreen.tv/blog/twitch-statistics/"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://www.uscreen.tv/wp-content/uploads/2022/06/twitch-statistics-hero.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Most watched games on Twitch - SullyGnome",
|
||||
"htmlTitle": "<b>Most watched games on Twitch</b> - SullyGnome",
|
||||
"link": "https://sullygnome.com/games/30/watched",
|
||||
"displayLink": "sullygnome.com",
|
||||
"snippet": "Statistics the most watched games on Twitch in the past 30 days. ... Most watched · Most streamed · Peak viewers · Peak channels · Viewer distribution.",
|
||||
"htmlSnippet": "Statistics the <b>most watched games on Twitch</b> in the past 30 days. ... Most watched · Most streamed · Peak viewers · Peak channels · Viewer distribution.",
|
||||
"cacheId": "8nUywrr01sQJ",
|
||||
"formattedUrl": "https://sullygnome.com/games/30/watched",
|
||||
"htmlFormattedUrl": "https://sullygnome.com/<b>games</b>/30/<b>watched</b>",
|
||||
"pagemap": {
|
||||
"metatags": [
|
||||
{
|
||||
"viewport": "width=device-width, initial-scale=1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Lost Ark peaks at 1.27M on launch day, becomes most-watched ...",
|
||||
"htmlTitle": "Lost Ark peaks at 1.27M on launch day, becomes <b>most</b>-<b>watched</b> ...",
|
||||
"link": "https://www.invenglobal.com/articles/16393/lost-ark-peaks-at-127m-on-launch-day-becomes-most-watched-game-on-twitch",
|
||||
"displayLink": "www.invenglobal.com",
|
||||
"snippet": "Feb 9, 2022 ... Lost Ark: Source: Smilegate Lost Ark is now the most watched game on Twitch. According to analytics website TwitchTracker.",
|
||||
"htmlSnippet": "Feb 9, 2022 <b>...</b> Lost Ark: Source: Smilegate Lost Ark is now the <b>most watched game on Twitch</b>. According to analytics website <b>TwitchTracker</b>.",
|
||||
"cacheId": "3xZkfSBOVfQJ",
|
||||
"formattedUrl": "https://www.invenglobal.com/.../lost-ark-peaks-at-127m-on-launch-day- becomes-most-watched-game-on-twitch",
|
||||
"htmlFormattedUrl": "https://www.invenglobal.com/.../lost-ark-peaks-at-127m-on-launch-day- becomes-<b>most</b>-<b>watched</b>-<b>game-on-twitch</b>",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcQYWr9o2ASOTxPBGCmZTOjThHjjHbmeLppGDy8dA0g8yeXkxafIm6d8X78",
|
||||
"width": "300",
|
||||
"height": "168"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"og:image": "https://static.invenglobal.com/upload/image/2022/02/09/r1644433519704165.jpeg",
|
||||
"twitter:card": "summary_large_image",
|
||||
"twitter:title": "Lost Ark peaks at 1.27M on launch day, becomes most-watched game on Twitch",
|
||||
"og:type": "article",
|
||||
"article:published_time": "2022-02-09T18:34:05+00:00",
|
||||
"article:section": "Lost Ark",
|
||||
"og:site_name": "InvenGlobal",
|
||||
"twitter:url": "https://www.invenglobal.com/articles/16393/lost-ark-peaks-at-127m-on-launch-day-becomes-most-watched-game-on-twitch",
|
||||
"og:title": "Lost Ark peaks at 1.27M on launch day, becomes most-watched game on Twitch",
|
||||
"og:date": "Feb 9, 2022",
|
||||
"_token": "BoHbtAOpx952okGPGYiMFoE508xwElpOsmruvfEB",
|
||||
"title": "Lost Ark peaks at 1.27M on launch day, becomes most-watched game on Twitch - Inven Global",
|
||||
"twitter:creator": "@InvenGlobal",
|
||||
"og:description": "Source: Smilegate Lost Ark is now the most watched game on Twitch. According to analytics website TwitchTracker.",
|
||||
"twitter:image": "https://static.invenglobal.com/upload/thumb/2022/02/09/w/b1644433519704165.jpg",
|
||||
"article:tag": "Asmongold",
|
||||
"fb:app_id": "173953323052298",
|
||||
"twitter:site": "@InvenGlobal",
|
||||
"article:modified_time": "2022-02-09T19:12:23+00:00",
|
||||
"viewport": "width=device-width, initial-scale=1.0",
|
||||
"twitter:description": "Source: Smilegate Lost Ark is now the most watched game on Twitch. According to analytics website TwitchTracker.",
|
||||
"og:author": "John Popko",
|
||||
"og:url": "https://www.invenglobal.com/articles/16393/lost-ark-peaks-at-127m-on-launch-day-becomes-most-watched-game-on-twitch"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://static.invenglobal.com/upload/image/2022/02/09/r1644433519704165.jpeg"
|
||||
}
|
||||
],
|
||||
"hatomfeed": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Twitch Revenue and Usage Statistics (2022) - Business of Apps",
|
||||
"htmlTitle": "<b>Twitch</b> Revenue and Usage Statistics (2022) - Business of Apps",
|
||||
"link": "https://www.businessofapps.com/data/twitch-statistics/",
|
||||
"displayLink": "www.businessofapps.com",
|
||||
"snippet": "Sep 6, 2022 ... Most viewed games on Twitch. League of Legends is by far the most popular game on Twitch, with Riot Games e-sports tournaments streamed on the ...",
|
||||
"htmlSnippet": "Sep 6, 2022 <b>...</b> <b>Most viewed games on Twitch</b>. League of Legends is by far the <b>most popular game on Twitch</b>, with Riot Games e-sports tournaments streamed on the ...",
|
||||
"cacheId": "O0iecpsbRoIJ",
|
||||
"formattedUrl": "https://www.businessofapps.com/data/twitch-statistics/",
|
||||
"htmlFormattedUrl": "https://www.businessofapps.com/data/<b>twitch</b>-statistics/",
|
||||
"pagemap": {
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRtN_0Lka-RbCSDVQ2YWOV_KHCGyQIu0NvKCX0aCLKKpqUlcOox5yFNME_2",
|
||||
"width": "278",
|
||||
"height": "181"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"og:image": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/uploads/2019/02/twitch.png",
|
||||
"msapplication-square70x70logo": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/themes/boa/lib/assets/images/mstile-70x70.png",
|
||||
"article:published_time": "2019-02-15T15:47:57+00:00",
|
||||
"twitter:card": "summary_large_image",
|
||||
"og:image:width": "292",
|
||||
"og:site_name": "Business of Apps",
|
||||
"twitter:label1": "Est. reading time",
|
||||
"msapplication-wide310x150logo": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/themes/boa/lib/assets/images/mstile-310x150.png",
|
||||
"og:image:type": "image/png",
|
||||
"msapplication-tileimage": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/themes/boa/lib/assets/images/mstile-144x144.png",
|
||||
"og:description": "Twitch is a livestreaming platform focused on video games. It was founded by Justin Kan in 2011, originally as a spin-off of Justin.tv. The latter started life in 2007 as a single channel, broadcasting Kan’s life live around the clock, pioneering the concept of ‘lifecasting’. The website attracted interest from others who were more interested in broadcasting their own lives than viewing that of Kan’s, which had nonetheless served as great exposure for Justin.TV. Acting on this interest, the site relaunched later in 2007, allowing users to create their own channels and broadcast their own content through the platform. Streaming games was not the original idea, but after seeing the interest from many users wanting to stream video games, the gaming category of Justin.TV was",
|
||||
"twitter:image": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/uploads/2019/02/twitch.png",
|
||||
"article:publisher": "https://www.facebook.com/thebusinessofapps",
|
||||
"twitter:data1": "4 minutes",
|
||||
"article_author": "Mansoor Iqbal",
|
||||
"twitter:site": "@businessofapps",
|
||||
"article:modified_time": "2022-09-06T13:15:15+01:00",
|
||||
"msapplication-square310x310logo": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/themes/boa/lib/assets/images/mstile-310x310.png",
|
||||
"msapplication-tilecolor": "#FFFFFF",
|
||||
"og:type": "article",
|
||||
"twitter:title": "Twitch Revenue and Usage Statistics (2022)",
|
||||
"og:title": "Twitch Revenue and Usage Statistics (2022)",
|
||||
"article_publisher": "Mansoor Iqbal",
|
||||
"og:image:height": "190",
|
||||
"og:updated_time": "2022-09-06T13:15:15+01:00",
|
||||
"msapplication-square150x150logo": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/themes/boa/lib/assets/images/mstile-150x150.png",
|
||||
"fb:app_id": "529576650555031",
|
||||
"viewport": "width=device-width, initial-scale=1",
|
||||
"twitter:description": "Twitch is a livestreaming platform focused on video games. It was founded by Justin Kan in 2011, originally as a spin-off of Justin.tv. The latter started life in 2007 as a single channel, broadcasting Kan’s life live around the clock, pioneering the concept of ‘lifecasting’. The website attracted interest from others who were more interested in broadcasting their own lives than viewing that of Kan’s, which had nonetheless served as great exposure for Justin.TV. Acting on this interest, the site relaunched later in 2007, allowing users to create their own channels and broadcast their own content through the platform. Streaming games was not the original idea, but after seeing the interest from many users wanting to stream video games, the gaming category of Justin.TV was",
|
||||
"og:locale": "en_US",
|
||||
"og:url": "https://www.businessofapps.com/data/twitch-statistics/"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://1z1euk35x7oy36s8we4dr6lo-wpengine.netdna-ssl.com/wp-content/uploads/2019/02/twitch.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "customsearch#result",
|
||||
"title": "Grand Theft Auto V in Top 5 Most Watched Games on Twitch in ...",
|
||||
"htmlTitle": "Grand Theft Auto V in Top 5 <b>Most Watched Games on Twitch</b> in ...",
|
||||
"link": "https://www.whatgadget.net/grand-theft-auto-v-in-top-5-most-watched-games-on-twitch-in-august-83-3m-hours-in-viewership-more-than-double-dota-2/",
|
||||
"displayLink": "www.whatgadget.net",
|
||||
"snippet": "Sep 20, 2020 ... Grand Theft Auto V (GTA V) was the fifth most-watched game on Twitch in August 2020. It had 112140 concurrent viewers on average.",
|
||||
"htmlSnippet": "Sep 20, 2020 <b>...</b> Grand Theft Auto V (GTA V) was the fifth <b>most</b>-<b>watched game on Twitch</b> in August 2020. It had 112140 concurrent viewers on average.",
|
||||
"cacheId": "CXPa3QP1wMsJ",
|
||||
"formattedUrl": "https://www.whatgadget.net/grand-theft-auto-v-in-top-5-most-watched-games -on-twitch-in-august-83-3m-hours-in-viewership-more-than-double-dota-2/",
|
||||
"htmlFormattedUrl": "https://www.whatgadget.net/grand-theft-auto-v-in-<b>top</b>-5-<b>most</b>-<b>watched</b>-<b>games -on-twitch</b>-in-august-83-3m-hours-in-viewership-<b>more</b>-than-double-dota-2/",
|
||||
"pagemap": {
|
||||
"hcard": [
|
||||
{
|
||||
"url_text": "What Gadget",
|
||||
"fn": "What Gadget",
|
||||
"photo": "https://cdn.whatgadget.net/wp-content/uploads/2020/09/05082235/WG-copy-440x440.png",
|
||||
"url": "https://www.whatgadget.net/author/what-gadget/"
|
||||
}
|
||||
],
|
||||
"cse_thumbnail": [
|
||||
{
|
||||
"src": "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcRuEymPx8cHe4O2txUXe18ka_XzAv6dbWN6WI9W6A4zutMDBKYw9CuKbmOc",
|
||||
"width": "329",
|
||||
"height": "153"
|
||||
}
|
||||
],
|
||||
"metatags": [
|
||||
{
|
||||
"og:image": "https://www.whatgadget.net/wp-content/uploads/2020/09/header.jpg",
|
||||
"og:type": "article",
|
||||
"article:published_time": "2020-09-20T13:22:35+00:00",
|
||||
"og:image:width": "460",
|
||||
"twitter:card": "summary_large_image",
|
||||
"og:site_name": "What Gadget",
|
||||
"og:title": "Grand Theft Auto V in Top 5 Most Watched Games on Twitch in August; 83.3M Hours in Viewership, More than Double Dota 2 - What Gadget",
|
||||
"og:image:height": "215",
|
||||
"twitter:label1": "Written by",
|
||||
"twitter:label2": "Estimated reading time",
|
||||
"og:image:type": "image/jpeg",
|
||||
"msapplication-tileimage": "https://cdn.whatgadget.net/wp-content/uploads/2020/09/05082311/cropped-WG-copy-e1599134950257-270x270.png",
|
||||
"og:description": "Grand Theft Auto V (GTA V) was the fifth most-watched game on Twitch in August 2020. It had 112,140 concurrent viewers on average.",
|
||||
"twitter:creator": "@WhatGadgetUK",
|
||||
"article:publisher": "https://www.facebook.com/WhatGadget.net",
|
||||
"twitter:data1": "What Gadget",
|
||||
"twitter:data2": "1 minute",
|
||||
"twitter:site": "@WhatGadgetUK",
|
||||
"article:modified_time": "2020-09-22T17:12:06+00:00",
|
||||
"viewport": "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1",
|
||||
"og:locale": "en_GB",
|
||||
"og:url": "https://www.whatgadget.net/grand-theft-auto-v-in-top-5-most-watched-games-on-twitch-in-august-83-3m-hours-in-viewership-more-than-double-dota-2/"
|
||||
}
|
||||
],
|
||||
"cse_image": [
|
||||
{
|
||||
"src": "https://www.whatgadget.net/wp-content/uploads/2020/09/header.jpg"
|
||||
}
|
||||
],
|
||||
"hatomfeed": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -17,6 +17,10 @@
|
|||
"@bbob/core": "^2.8.1",
|
||||
"@bbob/html": "^2.8.1",
|
||||
"@bbob/preset-html5": "^2.8.1",
|
||||
"@milkdown/core": "^6.4.1",
|
||||
"@milkdown/preset-commonmark": "^6.4.2",
|
||||
"@milkdown/prose": "^6.4.1",
|
||||
"@milkdown/theme-nord": "^6.4.1",
|
||||
"@vue-stripe/vue-stripe": "^4.4.2",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^4.5.2",
|
||||
|
@ -28,6 +32,7 @@
|
|||
"firebaseui": "^4.8.0",
|
||||
"lodash.chunk": "^4.2.0",
|
||||
"lodash.groupby": "^4.6.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.orderby": "^4.6.0",
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"marked": "^4.0.14",
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
<!-- TODO: translate strings -->
|
||||
<!-- TODO: allow for anonymous boards, prompt to sign up -->
|
||||
<!-- TODO: switch toggle -->
|
||||
<!-- TODO: add markdown wysiwyg -->
|
||||
<!-- TODO: add help section -->
|
||||
<!-- TODO: bring notifications back! -->
|
||||
<!-- TODO: fix favicon broken link -->
|
||||
<template>
|
||||
<div
|
||||
|
@ -103,7 +109,6 @@ export default {
|
|||
},
|
||||
|
||||
updateWallpaperUrl(value) {
|
||||
console.log(value);
|
||||
this.backgroundImageUrl = value;
|
||||
},
|
||||
|
||||
|
@ -116,7 +121,7 @@ export default {
|
|||
},
|
||||
|
||||
init() {
|
||||
this.$store.dispatch('LOAD_IGDB_PLATFORMS');
|
||||
// TODO: get platforms from constants
|
||||
|
||||
if (this.isPublicRoute) {
|
||||
return;
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
<!-- TODO: clone/fork board -->
|
||||
<!-- TODO: like/favorite board -->
|
||||
<template lang="html">
|
||||
<div
|
||||
:class="['board px-3 pb-3', { dragging, empty }]"
|
||||
>
|
||||
<div>
|
||||
<board-placeholder v-if="loading" />
|
||||
|
||||
<template v-else-if="showBoard">
|
||||
|
@ -37,17 +35,8 @@
|
|||
</b-button>
|
||||
</portal>
|
||||
|
||||
<game-list
|
||||
v-for="(list, listIndex) in board.lists"
|
||||
:list="list"
|
||||
:listIndex="listIndex"
|
||||
:key="list.name"
|
||||
/>
|
||||
|
||||
<add-list
|
||||
v-if="isBoardOwner"
|
||||
:empty="empty"
|
||||
/>
|
||||
<basic-board v-if="board.type === 'basic'" />
|
||||
<kanban-board v-else />
|
||||
</template>
|
||||
|
||||
<b-alert
|
||||
|
@ -62,16 +51,16 @@
|
|||
|
||||
<script>
|
||||
import BoardPlaceholder from '@/components/Board/BoardPlaceholder';
|
||||
import AddList from '@/components/Board/AddList';
|
||||
import GameList from '@/components/Lists/GameList';
|
||||
import KanbanBoard from '@/components/Board/KanbanBoard';
|
||||
import BasicBoard from '@/components/Board/BasicBoard';
|
||||
import chunk from 'lodash.chunk';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameList,
|
||||
BoardPlaceholder,
|
||||
AddList,
|
||||
KanbanBoard,
|
||||
BasicBoard,
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -99,10 +88,6 @@ export default {
|
|||
return this.$route.params?.id;
|
||||
},
|
||||
|
||||
empty() {
|
||||
return this.board?.lists?.length === 0;
|
||||
},
|
||||
|
||||
isBoardCached() {
|
||||
return this.board.id === this.boardId;
|
||||
},
|
||||
|
|
|
@ -26,27 +26,9 @@
|
|||
/>
|
||||
</b-form-group>
|
||||
|
||||
<!-- <b-form-group
|
||||
label="Board template"
|
||||
>
|
||||
<b-form-radio-group
|
||||
v-model="selectedTemplate"
|
||||
:options="boardTemplatesOptions"
|
||||
name="radios-btn-default"
|
||||
description="Optional"
|
||||
/>
|
||||
|
||||
<b-row v-if="selectedTemplate" class="mt-3">
|
||||
<b-col v-for="column in boardTemplates[selectedTemplate]" :key="column">
|
||||
<b-card
|
||||
:header="column"
|
||||
header-tag="header"
|
||||
header-class="p-1 pl-2"
|
||||
hide-footer
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form-group> -->
|
||||
<pre>
|
||||
{{ board.type }}
|
||||
</pre>
|
||||
|
||||
<b-button
|
||||
variant="primary"
|
||||
|
@ -69,20 +51,10 @@ export default {
|
|||
name: '',
|
||||
description: '',
|
||||
lists: [],
|
||||
type: 'kanban',
|
||||
},
|
||||
saving: false,
|
||||
selectedTemplate: null,
|
||||
// boardTemplatesOptions: [
|
||||
// { value: null, text: 'Blank' },
|
||||
// { value: 'standard', text: 'Standard' },
|
||||
// { value: 'detailed', text: 'Detailed' },
|
||||
// { value: 'completionist', text: 'Completionist' },
|
||||
// ],
|
||||
// boardTemplates: {
|
||||
// standard: ['Owned', 'Wishlist'],
|
||||
// detailed: ['Physical', 'Digital', 'Wishlist'],
|
||||
// completionist: ['Owned', 'Playing', 'Completed'],
|
||||
// },
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -94,16 +66,17 @@ export default {
|
|||
// TODO: put default board in constant
|
||||
const payload = {
|
||||
...this.board,
|
||||
// TODO: set default lists based on board type
|
||||
games: [],
|
||||
lastUpdated: Date.now(),
|
||||
lists: [{
|
||||
name: 'Click to rename',
|
||||
games: [358],
|
||||
games: [],
|
||||
settings: {
|
||||
showReleaseDates: false,
|
||||
sortOrder: 'sortByCustom',
|
||||
showGameTags: false,
|
||||
showGameNotes: false,
|
||||
showGameProgress: false,
|
||||
highlightCompletedGames: false,
|
||||
showGameCount: false,
|
||||
view: 'single'
|
||||
},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<!-- TODO: refactor saving, create payload locally -->
|
||||
<template lang="html">
|
||||
<section>
|
||||
<b-container>
|
||||
|
@ -44,6 +45,17 @@
|
|||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
label="Board type"
|
||||
label-for="name"
|
||||
>
|
||||
<b-dropdown id="dropdown-1" :text="board.type">
|
||||
<b-dropdown-item @click="setBoardType('kanban')">Kanban</b-dropdown-item>
|
||||
<!-- <b-dropdown-item @click="setBoardType('tiers')">Tiers</b-dropdown-item> -->
|
||||
<b-dropdown-item @click="setBoardType('basic')">Basic</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
:label="$t('board.settings.descriptionLabel')"
|
||||
label-for="description"
|
||||
|
@ -145,7 +157,6 @@ import { mapState, mapGetters } from 'vuex';
|
|||
import WallpapersList from '@/components/WallpapersList';
|
||||
import VSwatches from 'vue-swatches'
|
||||
import MiniBoard from '@/components/Board/MiniBoard';
|
||||
import orderby from 'lodash.orderby';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -180,10 +191,17 @@ export default {
|
|||
|
||||
this.board = await this.$store.dispatch('LOAD_BOARD', this.boardId);
|
||||
|
||||
if (!this.board.type) this.board.type = 'kanban';
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
setBoardType(type) {
|
||||
console.log('type', type);
|
||||
// TODO: check if switching to basic while having more than 1 list
|
||||
this.board.type = type;
|
||||
},
|
||||
|
||||
confirmDelete() {
|
||||
this.$bvModal.msgBoxConfirm('Are you sure you want to delete this board?', {
|
||||
title: 'Delete board',
|
||||
|
@ -208,7 +226,7 @@ export default {
|
|||
|
||||
this.loading = false;
|
||||
this.$bvToast.toast('Board removed');
|
||||
this.$router.push({ name: 'home' });
|
||||
this.$router.push({ name: 'boards' });
|
||||
},
|
||||
|
||||
selectWallpaper(wallpaper) {
|
||||
|
@ -219,17 +237,7 @@ export default {
|
|||
async saveBoard() {
|
||||
this.saving = true;
|
||||
|
||||
// const { board } = this;
|
||||
//
|
||||
// const payload = {
|
||||
// ...board,
|
||||
// description: this.description,
|
||||
// name: this.name,
|
||||
// isPublic: this.isPublic,
|
||||
// theme: this.theme,
|
||||
// };
|
||||
|
||||
// this.$store.commit('SET_ACTIVE_BOARD', this.board);
|
||||
this.board.lastUpdated = Date.now();
|
||||
|
||||
await this.$store.dispatch('SAVE_BOARD')
|
||||
.catch(() => {
|
||||
|
|
|
@ -120,14 +120,14 @@
|
|||
|
||||
<!-- TODO: add release date styles: countdown/simple date -->
|
||||
|
||||
<b-form-checkbox
|
||||
<!-- <b-form-checkbox
|
||||
v-model="list.settings.showGameProgress"
|
||||
name="check-button"
|
||||
class="mb-2"
|
||||
switch
|
||||
>
|
||||
{{ $t('board.list.showGameProgress') }}
|
||||
</b-form-checkbox>
|
||||
</b-form-checkbox> -->
|
||||
|
||||
<b-form-checkbox
|
||||
v-model="list.settings.highlightCompletedGames"
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<b-dropdown
|
||||
no-caret
|
||||
toggle-class="p-0 px-2 mt-n2"
|
||||
size="sm"
|
||||
>
|
||||
<template #button-content>
|
||||
<i class="fa fa-plus small pr-1" aria-hidden="true" />
|
||||
|
@ -66,7 +65,6 @@ export default {
|
|||
computed: {
|
||||
...mapState(['games', 'boards', 'wallpapers']),
|
||||
|
||||
// TODO: handle this at action/mutation level OR use getter at least
|
||||
filteredBoards() {
|
||||
return this.boards
|
||||
.filter(({ name }) => name.toLowerCase().includes(this.searchText.toLowerCase()));
|
||||
|
|
34
src/components/Board/BasicBoard.vue
Normal file
34
src/components/Board/BasicBoard.vue
Normal file
|
@ -0,0 +1,34 @@
|
|||
<template lang="html">
|
||||
<div class="basic-board my-3">
|
||||
<basic-game-list :list="list" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import BasicGameList from '@/components/Lists/BasicGameList';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BasicGameList,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['board']),
|
||||
|
||||
list() {
|
||||
const [firstList] = this.board?.lists;
|
||||
|
||||
return firstList || [];
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
.basic-board {
|
||||
width: 600px;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
40
src/components/Board/KanbanBoard.vue
Normal file
40
src/components/Board/KanbanBoard.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<template lang="html">
|
||||
<div class="board px-3 pb-3">
|
||||
<game-list
|
||||
v-for="(list, listIndex) in board.lists"
|
||||
:list="list"
|
||||
:listIndex="listIndex"
|
||||
:key="list.name"
|
||||
/>
|
||||
|
||||
<add-list
|
||||
v-if="isBoardOwner"
|
||||
:empty="empty"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import AddList from '@/components/Board/AddList';
|
||||
import GameList from '@/components/Lists/GameList';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameList,
|
||||
AddList,
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['board']),
|
||||
...mapGetters(['isBoardOwner']),
|
||||
|
||||
empty() {
|
||||
return this.board?.lists?.length === 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
</style>
|
|
@ -1,10 +1,13 @@
|
|||
<!-- TODO: make steam description match standard desc -->
|
||||
<!-- TODO: http://localhost:4000/g/114146/angry-video-game-nerd-i-and-ii-deluxe -->
|
||||
<template lang="html">
|
||||
<div class="game-description">
|
||||
<div :class="['game-description', source]">
|
||||
<b-spinner v-if="loading" class="spinner-centered" />
|
||||
|
||||
<template v-else>
|
||||
<div v-html="description" />
|
||||
<span class="text-muted mt-n3 mb-3">Source: {{ source }}</span>
|
||||
<!-- TODO: link to source -->
|
||||
<span class="text-muted mt-n3 mb-3 text-capitalize">Source: {{ source }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -29,8 +32,8 @@ export default {
|
|||
},
|
||||
|
||||
source() {
|
||||
if (this.wikipediaExtract) return 'Wikipedia';
|
||||
if (this.steamDescription) return 'Steam';
|
||||
if (this.wikipediaExtract) return 'wikipedia';
|
||||
if (this.steamDescription) return 'steam';
|
||||
|
||||
return 'IGDB';
|
||||
},
|
||||
|
@ -78,6 +81,10 @@ export default {
|
|||
|
||||
<style lang="scss" rel="stylesheet/scss">
|
||||
.game-description {
|
||||
&.steam {
|
||||
// Steam overrides
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template lang="html">
|
||||
d<template lang="html">
|
||||
<b-card class="mt-3 small">
|
||||
<!-- TODO: merge release Dates and platofmrs -->
|
||||
|
||||
<div v-if="gameGenres" class="pr-2 pb-2">
|
||||
<strong>Genres:</strong>
|
||||
<strong class="text-muted">Genres:</strong>
|
||||
|
||||
{{ gameGenres }}
|
||||
</div>
|
||||
|
@ -30,11 +30,18 @@
|
|||
<div class="pr-2 pb-2">
|
||||
<strong class="text-muted">Available for: </strong>
|
||||
|
||||
<span class="text-wrap">{{ gamePlatforms || 'N/A' }}</span>
|
||||
<span
|
||||
v-for="(platform, index) in gamePlatforms"
|
||||
:key="platform.id"
|
||||
>
|
||||
<b-link :to="{ name: 'platform', params: { id: platform.id }}">{{ platform.name }}</b-link>
|
||||
<template v-if="index < gamePlatforms.length - 1">, </template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="pr-2 pb-2">
|
||||
<strong>{{ $t('board.gameModal.releaseDate') }}</strong>
|
||||
<!-- TODO: merge release Dates and platforms -->
|
||||
<!-- <div class="pr-2 pb-2">
|
||||
<strong class="text-muted">{{ $t('board.gameModal.releaseDate') }}</strong>
|
||||
<ol v-if="releaseDates" class="list-unstyled mb-0">
|
||||
<li
|
||||
v-for="{ id, platform, date } in releaseDates"
|
||||
|
@ -47,8 +54,33 @@
|
|||
<div v-else>
|
||||
Not released yet
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="pr-2 pb-2">
|
||||
<strong class="text-muted">Tags</strong>
|
||||
|
||||
<br />
|
||||
|
||||
<b-button
|
||||
v-for="({ bgColor, textColor, name, index }) in tagsApplied"
|
||||
:key="index"
|
||||
rounded
|
||||
size="sm"
|
||||
variant="transparent"
|
||||
class="mr-1 mb-2"
|
||||
:style="`background-color: ${bgColor}; color: ${textColor}`"
|
||||
:to="{ name: 'tag.edit', params: { id: index } }"
|
||||
>
|
||||
<i class="fa-solid fa-tag mr-1" />
|
||||
{{ name }}
|
||||
</b-button>
|
||||
|
||||
<game-tags-dropdown v-if="user" />
|
||||
</div>
|
||||
|
||||
<strong class="text-muted">Other links</strong>
|
||||
<br>
|
||||
|
||||
<b-button
|
||||
v-for="{ url, id, icon, svg } in gameLinks"
|
||||
:href="url"
|
||||
|
@ -77,14 +109,34 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters, mapState } from 'vuex';
|
||||
import GameTagsDropdown from '@/components/Game/GameTagsDropdown';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GameTagsDropdown,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
progress: 0,
|
||||
saving: false,
|
||||
deleting: false,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['platformNames', 'gameLinks']),
|
||||
...mapState(['game']),
|
||||
...mapState(['game', 'tags', 'user', 'progresses']),
|
||||
|
||||
tagsApplied() {
|
||||
if (!this.tags) return [];
|
||||
|
||||
return this.tags?.map((tag, index) => ({ ...tag, index }))
|
||||
.filter((tag) => tag?.games?.includes(this.game?.id));
|
||||
},
|
||||
|
||||
gamePlatforms() {
|
||||
return this.game?.platforms?.map(({ name }) => name).join(', ');
|
||||
return this.game?.platforms;
|
||||
},
|
||||
|
||||
gameDevelopers() {
|
||||
|
@ -135,5 +187,50 @@ export default {
|
|||
});
|
||||
},
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
if (!this.tags) {
|
||||
await this.$store.dispatch('LOAD_TAGS');
|
||||
}
|
||||
|
||||
this.progress = this.progresses?.[this.game?.id]
|
||||
? JSON.parse(JSON.stringify(this.progresses?.[this.game?.id]))
|
||||
: 0;
|
||||
},
|
||||
|
||||
methods: {
|
||||
async deleteProgress() {
|
||||
const { id, name } = this.game;
|
||||
|
||||
this.deleting = true;
|
||||
|
||||
this.$store.commit('REMOVE_GAME_PROGRESS', id);
|
||||
|
||||
await this.$store.dispatch('SAVE_PROGRESSES_NO_MERGE')
|
||||
.catch(() => {
|
||||
this.$bvToast.toast('There was an error deleting your progress', { title: `${name} progress`, variant: 'error' });
|
||||
this.deleting = false;
|
||||
});
|
||||
|
||||
this.deleting = false;
|
||||
},
|
||||
|
||||
async saveProgress() {
|
||||
this.saving = true;
|
||||
|
||||
this.$store.commit('SET_GAME_PROGRESS', {
|
||||
progress: this.progress,
|
||||
gameId: this.game?.id,
|
||||
});
|
||||
|
||||
await this.$store.dispatch('SAVE_PROGRESSES')
|
||||
.catch(() => {
|
||||
this.saving = false;
|
||||
this.$bvToast.toast('There was an error saving your progress', { variant: 'error' });
|
||||
});
|
||||
|
||||
this.saving = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
<template lang="html">
|
||||
<div v-if="boardsWithGame.length" class="mt-4">
|
||||
<p class="small mb-2">Found in</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<b-button
|
||||
v-for="board in boardsWithGame"
|
||||
:to="{ name: 'board', params: { id: board.id } }"
|
||||
:key="board.id"
|
||||
variant="light"
|
||||
size="sm"
|
||||
:to="{ name: 'board', params: { id: board.id } }"
|
||||
variant="outline-primary"
|
||||
class="mr-2 py-0 mb-2"
|
||||
>
|
||||
<small>{{ board.name }}</small>
|
||||
|
@ -30,8 +27,9 @@ export default {
|
|||
...mapState(['board', 'game', 'boards']),
|
||||
|
||||
boardsWithGame() {
|
||||
return this.boards
|
||||
?.filter(({ lists }) => lists.some(({ games }) => games.includes(this.game.id))) || [];
|
||||
const filteredBoards = this.boards?.filter(({ lists }) => lists.some(({ games }) => games.includes(this.game.id))) || [];
|
||||
|
||||
return filteredBoards;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,33 +1,45 @@
|
|||
<template lang="html">
|
||||
<div class="mt-3 d-flex flex-wrap">
|
||||
<div
|
||||
v-for="({ imageUrl, isVideo, isCover }, index) in gameMedia"
|
||||
v-show="index > 0"
|
||||
:key="index"
|
||||
class="mr-2 align-items-center text-center mb-2 rounded cursor-pointer position-relative"
|
||||
>
|
||||
<i
|
||||
v-if="isVideo"
|
||||
class="fa-solid fa-play video-indicator position-absolute text-white"
|
||||
/>
|
||||
<div class="mt-3">
|
||||
<b-row no-gutters>
|
||||
<b-col
|
||||
v-for="({ imageUrl, isVideo, isCover }, index) in previewThumbs"
|
||||
:key="index"
|
||||
cols="3"
|
||||
>
|
||||
<div
|
||||
class="mr-2 align-items-center text-center mb-2 rounded cursor-pointer position-relative"
|
||||
>
|
||||
<i
|
||||
v-if="isVideo"
|
||||
class="fa-solid fa-play video-indicator position-absolute text-white"
|
||||
/>
|
||||
|
||||
<div v-if="isCover" class="position-absolute cover-indicator text-light small w-100 bg-dark rounded-bottom">
|
||||
Cover
|
||||
</div>
|
||||
<div v-if="isCover" class="position-absolute cover-indicator text-light small w-100 bg-dark rounded-bottom">
|
||||
Cover
|
||||
</div>
|
||||
|
||||
<b-img
|
||||
:src="imageUrl"
|
||||
rounded
|
||||
height="80"
|
||||
@click="viewMedia(index)"
|
||||
/>
|
||||
</div>
|
||||
<b-img
|
||||
:src="imageUrl"
|
||||
rounded
|
||||
fluid
|
||||
@click="viewMedia(index)"
|
||||
/>
|
||||
</div>
|
||||
</b-col>
|
||||
|
||||
<b-button
|
||||
v-if="totalMedia > 3"
|
||||
@click="viewMedia(3)"
|
||||
>
|
||||
<i class="fa-solid fa-photo-film" />
|
||||
{{ totalMedia - 3 }} more
|
||||
</b-button>
|
||||
</b-row>
|
||||
|
||||
<b-modal
|
||||
id="mediaModal"
|
||||
centered
|
||||
hide-footer
|
||||
size="xl"
|
||||
:visible="visible"
|
||||
@show="open"
|
||||
@hidden="close"
|
||||
|
@ -56,7 +68,7 @@
|
|||
|
||||
<div v-if="selectedMedia && gameMedia.length" class="game-media">
|
||||
<b-embed
|
||||
v-if="selectedMedia && selectedMedia.isVideo"
|
||||
v-if="isSelectedMediaVideo"
|
||||
type="iframe"
|
||||
aspect="16by9"
|
||||
:src="selectedMedia.videoUrl"
|
||||
|
@ -71,7 +83,6 @@
|
|||
/>
|
||||
|
||||
<footer class="mt-2 d-flex overflow-auto pb-2">
|
||||
<pre class="bg-success">{{ activeIndex }}</pre>
|
||||
<b-img
|
||||
v-for="(media, index) in gameMedia"
|
||||
:key="media.imageUrl"
|
||||
|
@ -97,7 +108,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
activeIndex: null,
|
||||
maxThumbnails: 3,
|
||||
maxThumbnails: 4,
|
||||
saving: false,
|
||||
};
|
||||
},
|
||||
|
@ -106,10 +117,24 @@ export default {
|
|||
...mapGetters(['isBoardOwner', 'gameMedia']),
|
||||
...mapState(['board', 'game']),
|
||||
|
||||
previewThumbs() {
|
||||
const previewThumbs = this.gameMedia.slice(0, this.maxThumbnails);
|
||||
|
||||
return previewThumbs;
|
||||
},
|
||||
|
||||
isSelectedMediaVideo() {
|
||||
return this.selectedMedia?.isVideo;
|
||||
},
|
||||
|
||||
selectedMedia() {
|
||||
return this.gameMedia?.[this.activeIndex];
|
||||
},
|
||||
|
||||
totalMedia() {
|
||||
return this.gameMedia?.length || 0;
|
||||
},
|
||||
|
||||
visible() {
|
||||
return this.activeIndex !== null;
|
||||
},
|
||||
|
@ -158,7 +183,7 @@ export default {
|
|||
.game-media {
|
||||
display: grid;
|
||||
grid-gap: 1rem;
|
||||
grid-template-rows: 50vh auto;
|
||||
grid-template-rows: 1fr auto;
|
||||
}
|
||||
|
||||
.selected-image {
|
||||
|
@ -173,4 +198,9 @@ export default {
|
|||
.cover-indicator {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.media-button {
|
||||
padding: 21px 16px;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
40
src/components/Game/GameProgress.vue
Normal file
40
src/components/Game/GameProgress.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<template lang="html">
|
||||
<div>
|
||||
<strong class="text-muted">Completed: {{ progress }}</strong>
|
||||
|
||||
<b-form-input
|
||||
size="lg"
|
||||
v-model="progress"
|
||||
type="range"
|
||||
max="100"
|
||||
step="1"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
variant="primary"
|
||||
:disabled="saving"
|
||||
class="mr-2"
|
||||
@click="saveProgress"
|
||||
>
|
||||
<b-spinner small v-if="saving" />
|
||||
<span v-else>{{ $t('global.save') }}</span>
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
:disabled="deleting"
|
||||
variant="danger"
|
||||
@click="deleteProgress"
|
||||
>
|
||||
<b-spinner small v-if="deleting" />
|
||||
<i v-else class="fas fa-trash fa-fw" aria-hidden />
|
||||
</b-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
</style>
|
|
@ -61,12 +61,12 @@ export default {
|
|||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
.similar-games {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-columns: repeat(10, 1fr);
|
||||
grid-gap: 1rem;
|
||||
margin-bottom: 20vh;
|
||||
|
||||
@media(max-width: 780px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
sm="6"
|
||||
md="4"
|
||||
lg="3"
|
||||
class="px-3 pb-4"
|
||||
class="px-1 pb-2"
|
||||
>
|
||||
<mini-board
|
||||
:board="board"
|
||||
|
|
17
src/components/GameCards/GameCard.vue
Normal file
17
src/components/GameCards/GameCard.vue
Normal file
|
@ -0,0 +1,17 @@
|
|||
<template lang="html">
|
||||
<div>
|
||||
game card here
|
||||
type: <pre>{{ type }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
type: string,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
</style>
|
155
src/components/Lists/BasicGameList.vue
Normal file
155
src/components/Lists/BasicGameList.vue
Normal file
|
@ -0,0 +1,155 @@
|
|||
<template lang="html">
|
||||
<draggable
|
||||
class="games centered"
|
||||
handle=".card"
|
||||
ghost-class="card-placeholder"
|
||||
drag-class="border-selected"
|
||||
chosen-class="border-primary"
|
||||
filter=".drag-filter"
|
||||
delay="50"
|
||||
animation="500"
|
||||
:list="list.games"
|
||||
:move="validateMove"
|
||||
:disabled="draggingDisabled"
|
||||
:group="{ name: 'games' }"
|
||||
@end="dragEnd"
|
||||
@start="dragStart"
|
||||
>
|
||||
<b-card
|
||||
no-body
|
||||
class="mb-2 flex-row align-items-center cursor-pointer"
|
||||
v-for="gameId in sortedGames"
|
||||
:key="gameId"
|
||||
@click="openGame(gameId, list)"
|
||||
>
|
||||
<b-img
|
||||
:src="$options.getThumbnailUrl(games[gameId])"
|
||||
alt="Image"
|
||||
class="m-2"
|
||||
rounded
|
||||
fluid
|
||||
width="160"
|
||||
/>
|
||||
|
||||
<span class="d-flex w-100 justify-content-center mr-2">{{ games[gameId].name }}</span>
|
||||
</b-card>
|
||||
|
||||
<div v-if="isEmpty && isBoardOwner">
|
||||
<b-button
|
||||
variant="light"
|
||||
block
|
||||
class="mb-2"
|
||||
:disabled="!isBoardOwner"
|
||||
:to="{ name: 'search', query: { boardId: board.id, listIndex: 0 } }"
|
||||
>
|
||||
<template v-if="isBoardOwner">Add games</template>
|
||||
<template v-else>Empty list</template>
|
||||
</b-button>
|
||||
</div>
|
||||
</draggable>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable';
|
||||
import slugify from 'slugify'
|
||||
import orderby from 'lodash.orderby';
|
||||
import { DEFAULT_LIST_VIEW } from '@/constants';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import { getThumbnailUrl } from '@/utils';
|
||||
|
||||
export default {
|
||||
getThumbnailUrl,
|
||||
|
||||
components: {
|
||||
draggable,
|
||||
},
|
||||
|
||||
props: {
|
||||
list: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
draggingId: null,
|
||||
editing: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['games', 'dragging', 'progresses', 'board', 'user', 'settings']),
|
||||
...mapGetters(['isBoardOwner']),
|
||||
|
||||
draggingDisabled() {
|
||||
return !this.user || !this.isBoardOwner;
|
||||
},
|
||||
|
||||
autoSortEnabled() {
|
||||
return ['sortByName', 'sortByRating', 'sortByReleaseDate', 'sortByProgress'].includes(this.list?.settings?.sortOrder);
|
||||
},
|
||||
|
||||
sortedGames() {
|
||||
const { settings, games } = this.list;
|
||||
const sortOrder = settings.sortOrder || 'sortByCustom';
|
||||
|
||||
switch (sortOrder) {
|
||||
case 'sortByCustom': return this.list.games;
|
||||
case 'sortByProgress': return orderby(games, [game => this.progresses[game] || 0], ['desc']);
|
||||
case 'sortByRating': return orderby(games, [game => this.games[game].rating || 0], ['desc']);
|
||||
case 'sortByName': return orderby(games, [game => this.games[game].name]);
|
||||
default:
|
||||
return this.list.games;
|
||||
}
|
||||
},
|
||||
|
||||
isEmpty() {
|
||||
return this.list.games.length === 0;
|
||||
},
|
||||
|
||||
singleList() {
|
||||
return this.board.lists.length === 1;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
openGame(id, list) {
|
||||
const slug = slugify(this.games[id].slug, { lower: true });
|
||||
|
||||
this.$router.push({
|
||||
name: 'game',
|
||||
params: {
|
||||
id,
|
||||
slug,
|
||||
boardId: this.board.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
validateMove({ from, to }) {
|
||||
const sameList = from.id === to.id;
|
||||
const notInList = !this.board?.lists?.[to.id]?.games?.includes(Number(this.draggingId));
|
||||
|
||||
return sameList || notInList && !sameList;
|
||||
},
|
||||
|
||||
dragStart({ item }) {
|
||||
this.$store.commit('SET_DRAGGING_STATUS', true);
|
||||
this.draggingId = item.id;
|
||||
},
|
||||
|
||||
dragEnd() {
|
||||
this.$store.commit('SET_DRAGGING_STATUS', false);
|
||||
this.saveBoard();
|
||||
},
|
||||
|
||||
async saveBoard() {
|
||||
await this.$store.dispatch('SAVE_BOARD')
|
||||
.catch(() => {
|
||||
this.$store.commit('SET_SESSION_EXPIRED', true);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -18,11 +18,11 @@
|
|||
<div class="align-items-center d-flex ml-auto mr-2">
|
||||
<portal-target name="headerActions" />
|
||||
|
||||
<!-- <b-button v-if="user" class="mr-2" variant="success" :to="{ name: 'upgrade' }">
|
||||
Upgrade
|
||||
</b-button> -->
|
||||
<b-button :to="{ name: 'search' }" class="d-sm-none">
|
||||
<i class="fa fa-search" aria-hidden="true"></i>
|
||||
</b-button>
|
||||
|
||||
<search-box />
|
||||
<search-box class="d-none d-sm-block" />
|
||||
|
||||
<b-button
|
||||
v-if="!user"
|
||||
|
|
|
@ -112,30 +112,6 @@ export const KEYBOARD_SHORTCUTS = {
|
|||
ROUTE_settings: ['shift', 's'],
|
||||
};
|
||||
|
||||
export const PLATFORM_FILTER_FIELDS = [
|
||||
null,
|
||||
'all',
|
||||
'console',
|
||||
// 'arcade',
|
||||
// 'platform',
|
||||
'operating_system',
|
||||
'portable_console',
|
||||
'computer',
|
||||
];
|
||||
|
||||
export const PLATFORM_SORT_FILEDS = [
|
||||
'generation',
|
||||
'name',
|
||||
];
|
||||
|
||||
export const PLATFORM_BG_HEX = {
|
||||
167: '#222',
|
||||
166: '#000',
|
||||
48: '#2e6db4',
|
||||
49: '#177d3e',
|
||||
130: '#ce181e',
|
||||
};
|
||||
|
||||
export const LIST_VIEWS = {
|
||||
single: 'Single',
|
||||
grid: 'Grid',
|
||||
|
|
|
@ -15,14 +15,6 @@ const routes = [
|
|||
title: 'Notes'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'game.progress',
|
||||
path: '/g/:id/:slug/progress',
|
||||
component: () => import(/* webpackChunkName: "game" */ '@/game/pages/GameProgressPage'),
|
||||
meta: {
|
||||
title: 'Track game progress'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'game',
|
||||
path: '/g/:id/:slug',
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
</portal>
|
||||
|
||||
<b-col cols="12" sm="6">
|
||||
<div ref="editor" />
|
||||
|
||||
<game-note
|
||||
v-if="showPreview"
|
||||
:note="{ note }"
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
cols="12"
|
||||
md="4"
|
||||
xl="3"
|
||||
class="text-center"
|
||||
>
|
||||
<b-img
|
||||
:src="gameCoverUrl"
|
||||
|
@ -34,27 +35,12 @@
|
|||
fluid
|
||||
/>
|
||||
|
||||
<game-note
|
||||
v-if="note"
|
||||
:note="note"
|
||||
class="cursor-pointer mt-3 d-none d-md-block"
|
||||
@click.native="$router.push({ name: 'game.notes', params: { id: game.id, slug: game.slug } })"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
v-else
|
||||
size="sm"
|
||||
variant="warning"
|
||||
:to="{ name: 'game.notes', params: { id: game.id, slug: game.slug } }"
|
||||
class="mt-2"
|
||||
>
|
||||
Add note
|
||||
</b-button>
|
||||
<game-media />
|
||||
|
||||
<b-button
|
||||
v-if="gameNews.length"
|
||||
size="sm"
|
||||
class="mt-2 ml-2"
|
||||
class="mt-2 ml-2 d-none d-md-block"
|
||||
:to="{ name: 'game.news', params: { id: game.id, slug: game.slug } }"
|
||||
>
|
||||
<b-badge>{{ gameNews.length }}</b-badge>
|
||||
|
@ -62,14 +48,8 @@
|
|||
</b-button>
|
||||
|
||||
<!-- <amazon-links class="mt-2" /> -->
|
||||
|
||||
<game-in-list :class="{ 'text-white': hasWallpaper }" />
|
||||
|
||||
<!-- <game-speedruns /> -->
|
||||
|
||||
<!-- <div v-if="gameAchievements"> -->
|
||||
<!-- <pre>{{ gameAchievements }}</pre> -->
|
||||
<!-- </div> -->
|
||||
<!-- <pre>{{ gameAchievements }}</pre> -->
|
||||
</b-col>
|
||||
|
||||
<b-col
|
||||
|
@ -80,46 +60,30 @@
|
|||
<article :class="[' rounded', hasWallpaper ? 'bg-white mt-2 mt-md-0 p-3' : 'px-sm-3 p-0']">
|
||||
<div class="d-flex justify-content-between">
|
||||
<game-titles />
|
||||
|
||||
<b-link
|
||||
class="align-self-end ml-2 small"
|
||||
:to="{ name: 'game.progress', params: { id: game.id, slug: game.slug } }"
|
||||
>
|
||||
<template v-if="progress > 0">
|
||||
{{ progress }}% completed
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
Set progress
|
||||
</template>
|
||||
</b-link>
|
||||
</div>
|
||||
|
||||
<template v-if="tagsApplied.length">
|
||||
<b-button
|
||||
v-for="({ bgColor, textColor, name, index }) in tagsApplied"
|
||||
:key="name"
|
||||
rounded
|
||||
size="sm"
|
||||
variant="transparent"
|
||||
class="mr-1 mb-2"
|
||||
:style="`background-color: ${bgColor}; color: ${textColor}`"
|
||||
:to="{ name: 'tag.edit', params: { id: index } }"
|
||||
>
|
||||
<i class="fa-solid fa-tag mr-1" />
|
||||
{{ name }}
|
||||
</b-button>
|
||||
|
||||
</template>
|
||||
|
||||
<game-tags-dropdown v-if="user" />
|
||||
|
||||
<aside class="supplemental-info bg-white field float-right ml-5 pb-2">
|
||||
<game-details />
|
||||
<game-note
|
||||
v-if="note"
|
||||
:note="note"
|
||||
class="cursor-pointer mt-3 d-none d-md-block"
|
||||
@click.native="$router.push({ name: 'game.notes', params: { id: game.id, slug: game.slug } })"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
v-else
|
||||
size="sm"
|
||||
variant="warning"
|
||||
:to="{ name: 'game.notes', params: { id: game.id, slug: game.slug } }"
|
||||
class="mt-2"
|
||||
>
|
||||
Add note
|
||||
</b-button>
|
||||
</aside>
|
||||
|
||||
<game-description />
|
||||
<game-media-viewer />
|
||||
<game-in-list :class="{ 'text-white': hasWallpaper }" />
|
||||
<game-ratings />
|
||||
</article>
|
||||
|
||||
|
@ -160,8 +124,7 @@ import { mapState, mapGetters } from 'vuex';
|
|||
import { WEBSITE_CATEGORIES } from '@/constants';
|
||||
// import AmazonLinks from '@/components/Game/AmazonLinks';
|
||||
import GameDetails from '@/components/Game/GameDetails';
|
||||
import GameTagsDropdown from '@/components/Game/GameTagsDropdown';
|
||||
import GameMediaViewer from '@/components/Game/GameMediaViewer';
|
||||
import GameMedia from '@/components/Game/GameMedia';
|
||||
import GameTitles from '@/components/Game/GameTitles';
|
||||
import GameRatings from '@/components/Game/GameRatings';
|
||||
import GameDescription from '@/components/Game/GameDescription';
|
||||
|
@ -177,9 +140,8 @@ export default {
|
|||
GameNote,
|
||||
GameDescription,
|
||||
GameDetails,
|
||||
GameTagsDropdown,
|
||||
GameTitles,
|
||||
GameMediaViewer,
|
||||
GameMedia,
|
||||
GameRatings,
|
||||
// GameSpeedruns,
|
||||
SimilarGames,
|
||||
|
@ -219,6 +181,7 @@ export default {
|
|||
|
||||
originBoardId() {
|
||||
return this.$route?.params?.boardId;
|
||||
return this.$route?.params?.boardId;
|
||||
},
|
||||
|
||||
// gameAchievements() {
|
||||
|
@ -229,13 +192,6 @@ export default {
|
|||
return this.notes[this.game?.id] || null;
|
||||
},
|
||||
|
||||
tagsApplied() {
|
||||
if (!this.tags) return [];
|
||||
|
||||
return this.tags?.map((tag, index) => ({ ...tag, index }))
|
||||
.filter((tag) => tag?.games?.includes(this.game?.id));
|
||||
},
|
||||
|
||||
legalNotice() {
|
||||
return this.game?.steam?.legal_notice;
|
||||
},
|
||||
|
@ -256,12 +212,6 @@ export default {
|
|||
: '/no-image.jpg';
|
||||
},
|
||||
|
||||
progress() {
|
||||
const { gameId, progresses } = this;
|
||||
|
||||
return progresses[gameId] || null;
|
||||
},
|
||||
|
||||
gameId() {
|
||||
return this.$route.params.id;
|
||||
},
|
||||
|
@ -288,9 +238,6 @@ export default {
|
|||
async mounted() {
|
||||
if (!this.twitchToken) return this.waitAndLoadGame();
|
||||
|
||||
if (!this.tags) {
|
||||
await this.$store.dispatch('LOAD_TAGS');
|
||||
}
|
||||
this.loadGame();
|
||||
},
|
||||
|
||||
|
@ -337,7 +284,7 @@ export default {
|
|||
|
||||
// TODO: find more precise way to load GOG game, based on id?
|
||||
const gogPage = this.game?.websites?.find(({ category }) => category !== GOG_CATEGORY_ID);
|
||||
if (gogPage) await this.$store.dispatch('LOAD_GOG_GAME', this.game.name).catch((e) => {});
|
||||
if (gogPage) await this.$store.dispatch('LOAD_GOG_GAME', this.game?.name).catch((e) => {});
|
||||
|
||||
// const wikipediaData = this.game?.websites?.find(({ url, category }) => url && category === WEBSITE_CATEGORIES.WIKIPEDIA);
|
||||
const wikipediaSlug = this.game?.websites
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
<!-- TODO: switch to modal or dropdown -->
|
||||
<template lang="html">
|
||||
<section>
|
||||
<b-container>
|
||||
<portal to="pageTitle">
|
||||
<div>
|
||||
<b-button
|
||||
:to="{ name: 'game', params: { id: game.id, slug: game.slug } }"
|
||||
variant="light"
|
||||
class="mr-2"
|
||||
>
|
||||
<i class="fa-solid fa-chevron-left" />
|
||||
</b-button>
|
||||
|
||||
Track progress
|
||||
</div>
|
||||
</portal>
|
||||
|
||||
|
||||
<b-row>
|
||||
<b-col cols="12" sm="6">
|
||||
<router-link :to="{ name: 'game', params: { id: game.id, slug: game.slug }}" class="float-right">
|
||||
<b-img :src="gameCoverUrl" fluid rounded />
|
||||
</router-link>
|
||||
</b-col>
|
||||
|
||||
<b-col cols="12" sm="6" class="mt-3 mt-sm-0 mb-3">
|
||||
<b-input-group :prepend="`${progress}%`" class="field mb-2">
|
||||
<b-form-input
|
||||
size="lg"
|
||||
v-model="progress"
|
||||
type="range"
|
||||
max="100"
|
||||
step="1"
|
||||
/>
|
||||
</b-input-group>
|
||||
|
||||
<b-button
|
||||
variant="primary"
|
||||
:disabled="saving"
|
||||
class="mr-2"
|
||||
@click="saveProgress"
|
||||
>
|
||||
<b-spinner small v-if="saving" />
|
||||
<span v-else>{{ $t('global.save') }}</span>
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
:disabled="deleting"
|
||||
variant="danger"
|
||||
@click="deleteProgress"
|
||||
>
|
||||
<b-spinner small v-if="deleting" />
|
||||
<i v-else class="fas fa-trash fa-fw" aria-hidden />
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { getGameCoverUrl } from '@/utils';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
progress: 0,
|
||||
saving: false,
|
||||
deleting: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['progresses', 'game']),
|
||||
|
||||
gameCoverUrl() {
|
||||
return getGameCoverUrl(this.game);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.progress = this.progresses?.[this.game?.id]
|
||||
? JSON.parse(JSON.stringify(this.progresses?.[this.game?.id]))
|
||||
: 0;
|
||||
},
|
||||
|
||||
methods: {
|
||||
async deleteProgress() {
|
||||
const { id, name } = this.game;
|
||||
|
||||
this.deleting = true;
|
||||
|
||||
this.$store.commit('REMOVE_GAME_PROGRESS', id);
|
||||
|
||||
await this.$store.dispatch('SAVE_PROGRESSES_NO_MERGE')
|
||||
.catch(() => {
|
||||
this.$bvToast.toast('There was an error deleting your progress', { title: `${name} progress`, variant: 'error' });
|
||||
this.deleting = false;
|
||||
});
|
||||
|
||||
this.deleting = false;
|
||||
this.$router.push({ name: 'game', params: { id: this.game.id, slug: this.game.slug }});
|
||||
},
|
||||
|
||||
async saveProgress() {
|
||||
this.saving = true;
|
||||
|
||||
this.$store.commit('SET_GAME_PROGRESS', {
|
||||
progress: this.progress,
|
||||
gameId: this.game?.id,
|
||||
});
|
||||
|
||||
await this.$store.dispatch('SAVE_PROGRESSES')
|
||||
.catch(() => {
|
||||
this.saving = false;
|
||||
this.$bvToast.toast('There was an error saving your progress', { variant: 'error' });
|
||||
});
|
||||
|
||||
this.saving = false;
|
||||
this.$router.push({ name: 'game', params: { id: this.game.id, slug: this.game.slug }});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,3 +1,5 @@
|
|||
// TODO: make one game card component, use props for type
|
||||
// TODO: disolve
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,22 +1,113 @@
|
|||
<!-- TODO: only load platforms once -->
|
||||
<!-- TODO: add infinite loader / pagination -->
|
||||
<!-- TODO: add sorting -->
|
||||
<template lang="html">
|
||||
<div>
|
||||
<pre>{{ platform }}</pre>
|
||||
</div>
|
||||
<section>
|
||||
<b-container>
|
||||
<b-spinner v-if="loading" class="spinner-centered" />
|
||||
|
||||
<section v-else-if="platform">
|
||||
<portal to="pageTitle">
|
||||
{{ platform.name }}
|
||||
</portal>
|
||||
|
||||
<portal to="headerActions">
|
||||
<b-button :to="{ name: 'platforms' }" class="mr-2">
|
||||
All platforms
|
||||
</b-button>
|
||||
</portal>
|
||||
|
||||
<b-row class="mb-5">
|
||||
<b-col v-for="game in platformGames" :key="game.id" cols="3">
|
||||
<game-card-search :game="game" />
|
||||
</b-col>
|
||||
|
||||
<b-button block class="mt-5 mb-5" @click="loadMoreGames">
|
||||
Load more games
|
||||
</b-button>
|
||||
</b-row>
|
||||
</section>
|
||||
</b-container>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import merge from 'lodash.merge';
|
||||
import GameCardSearch from '@/components/GameCards/GameCardSearch';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['platforms']),
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
platforms: [],
|
||||
platformGames: [],
|
||||
offset: 0,
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
GameCardSearch,
|
||||
},
|
||||
|
||||
computed: {
|
||||
platform() {
|
||||
return this.platforms.find(({ slug }) => slug === this.$route.params.slug);
|
||||
return this.platforms?.find(({ id }) => id == this.$route.params.id);
|
||||
},
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.platforms = await this.$store.dispatch('LOAD_IGDB_PLATFORMS');
|
||||
this.loadGames();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async loadGames() {
|
||||
this.loading = this.offset === 0;
|
||||
|
||||
const data = `
|
||||
fields platforms,slug,release_dates,rating,cover.image_id;
|
||||
limit 50;
|
||||
offset: ${this.offset};
|
||||
sort name asc;
|
||||
where release_dates.platform = ${this.platform.id};
|
||||
`;
|
||||
|
||||
console.log('this.offset', this.offset);
|
||||
|
||||
if (this.offset === 0) {
|
||||
this.platformGames = await this.$store.dispatch('IGDB', { path: 'games', data });
|
||||
} else {
|
||||
const games = await this.$store.dispatch('IGDB', { path: 'games', data });
|
||||
|
||||
console.log('games', games.length);
|
||||
console.log(typeof games);
|
||||
console.log('platformGames', this.platformGames);
|
||||
console.log(typeof this.platformGames);
|
||||
|
||||
this.platformGames = merge(this.platformGames, games);
|
||||
|
||||
console.log('this.platformGames', this.platformGames);
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
async loadMoreGames() {
|
||||
this.offset = this.platformGames.length;
|
||||
this.loadGames();
|
||||
// const data = `
|
||||
// fields platforms,slug,release_dates,rating,cover.image_id;
|
||||
// limit 50;
|
||||
// offset: ${this.offset};
|
||||
// sort name asc;
|
||||
// where release_dates.platform = ${this.platform.id};
|
||||
// `;
|
||||
//
|
||||
// const moreGames = await this.$store.dispatch('IGDB', { path: 'games', data });
|
||||
//
|
||||
// this.loading = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" rel="stylesheet/scss" scoped>
|
||||
</style>
|
||||
|
|
|
@ -1,126 +1,26 @@
|
|||
<!-- TODO: finish or kill it -->
|
||||
<template lang="html">
|
||||
<div>
|
||||
<b-button
|
||||
variant="link"
|
||||
block
|
||||
v-for="platform in platforms"
|
||||
:to="{ name: 'platform.page', params: { slug: platform.slug } }"
|
||||
:to="{ name: 'platform', params: { id: platform.id } }"
|
||||
:key="platform.id"
|
||||
>
|
||||
<img :src="`static/logos/platforms-new/${platform.slug}.png`" />
|
||||
{{ platform.slug }} | {{ platform.id }}
|
||||
{{ platform.name }}
|
||||
</b-button>
|
||||
|
||||
<!-- <img src="static/logos/platforms-new/3do.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/action-max.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/amiga-cd-3 2.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/arcadia-2001.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/astrocade.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/atari-2600.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/atari-5200.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/atari-7800.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/atari-xe.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/beena.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/cassette-vision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/cd-i.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/channel-f.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/coleco-vision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/commodore-cdtv.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/commodore64.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/cps-changer.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/creativision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/dreamcast.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/family-computer.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/fm-towns-marty.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/game-wave.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/gamecube.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/genesis-32x.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/genesis.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/gx4000.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/halcyon.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/hyper-scan.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/imagination-machine.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/intellivision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/interactive-vision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/ique.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/jaguar-cd.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/jaguar.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/konix.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/laser-active.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/leisure-vision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/loopy.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/mark-iii.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/mega-cd-ii.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/mega-cd.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/mega-drive.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/mega-ld.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/mp1000.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/my-vision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/neo-geo-cd.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/neo-geo.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/nes.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/nintendo-64-dd.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/nintendo-64.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/nuon.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/odyssey2.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/pc-engine.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/pc-fx.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/picno.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/pico.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/pippin.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/playdia.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/playstation.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/ps-2.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/ps3.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/ps4.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/pv-1000.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/rca-studio-ii.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/satellaview.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/sega-cd.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/sega-master-system.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/sega-saturn.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/sg-1000.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/socrates.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-acan.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-cassette-vision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-cd-rom.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-famicom.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-grafx.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-nintendo.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/super-vision-8000.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/switch.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/turbo-grafx-16.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/tutor.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/tv-boy.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/ultravision.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/v-flash.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/v-smile.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/vc4000.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/vectrex.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/video-art.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/video-challenger.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/video-driver.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/videopac.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/vis.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/wii-u.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/wii.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/xavix.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/xbox-360.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/xbox-one.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/xbox.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/zeebo.png" /> -->
|
||||
<!-- <img src="static/logos/platforms-new/zemina.png" /> -->
|
||||
<!-- <pre class="text-dark small">{{ platforms }}</pre> -->
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import orderby from 'lodash.orderby';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['platforms']),
|
||||
data() {
|
||||
return {
|
||||
platforms: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -129,10 +29,12 @@ export default {
|
|||
|
||||
methods: {
|
||||
async loadPlatforms() {
|
||||
await this.$store.dispatch('LOAD_IGDB_PLATFORMS')
|
||||
const platforms = await this.$store.dispatch('LOAD_IGDB_PLATFORMS')
|
||||
.catch(() => {
|
||||
this.$bvToast.toast('There was an error loading platforms', { variant: 'error' });
|
||||
});
|
||||
|
||||
this.platforms = orderby(platforms, 'name');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -8,8 +8,8 @@ const routes = [
|
|||
},
|
||||
},
|
||||
{
|
||||
path: '/platforms/:slug',
|
||||
name: 'platform.page',
|
||||
path: '/p/:id',
|
||||
name: 'platform',
|
||||
component: () => import(/* webpackChunkName: "platforms" */ '@/platforms/pages/PlatformPage'),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!-- TODO: use custom login page -->
|
||||
<!-- TODO: use custom login page, modal? -->
|
||||
<template lang="html">
|
||||
<section>
|
||||
<b-container>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<template lang="html">
|
||||
<b-container>
|
||||
<game-boards />
|
||||
<section class="pb-5">
|
||||
<b-container>
|
||||
<game-boards />
|
||||
|
||||
<!-- <div class="game-deals">
|
||||
<twitter-feed twitter-user="wario64" />
|
||||
</div> -->
|
||||
</b-container>
|
||||
<!-- <div class="game-deals">
|
||||
<twitter-feed twitter-user="wario64" />
|
||||
</div> -->
|
||||
</b-container>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -20,9 +20,14 @@
|
|||
variant="light"
|
||||
class="mr-2"
|
||||
right
|
||||
no-caret
|
||||
>
|
||||
<template #button-content>
|
||||
Filter <template v-if="selectedPlatforms.length">({{ selectedPlatforms.length }})</template>
|
||||
<i class="fa-solid fa-filter fa-fw" />
|
||||
|
||||
<b-badge v-if="selectedPlatforms.length">
|
||||
{{ selectedPlatforms.length }}
|
||||
</b-badge>
|
||||
</template>
|
||||
|
||||
<b-dropdown-item
|
||||
|
@ -45,45 +50,47 @@
|
|||
</b-dropdown>
|
||||
</portal>
|
||||
|
||||
<b-col cols="12" class="bg-light py-2 mb-3" v-if="activeBoard">
|
||||
<b-col cols="12" class="py-2 mb-3" v-if="activeBoard">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="d-none d-sm-block">
|
||||
Add games to:
|
||||
</span>
|
||||
|
||||
<b-button-group class="ml-sm-2">
|
||||
<b-dropdown
|
||||
split
|
||||
variant="light"
|
||||
:split-to="{ name: 'board', params: { id: boardId } }"
|
||||
:text="activeBoard.name"
|
||||
<b-dropdown
|
||||
split
|
||||
variant="light"
|
||||
size="sm"
|
||||
class="ml-2"
|
||||
:split-to="{ name: 'board', params: { id: boardId } }"
|
||||
:text="activeBoard.name"
|
||||
>
|
||||
<b-dropdown-item
|
||||
v-for="board in boards"
|
||||
:key="board.id"
|
||||
:disabled="!board.lists.length"
|
||||
:to="{ name: 'search', query: { boardId: board.id, listIndex: 0, q: query } }"
|
||||
>
|
||||
<b-dropdown-item
|
||||
v-for="board in boards"
|
||||
:key="board.id"
|
||||
:disabled="!board.lists.length"
|
||||
:to="{ name: 'search', query: { boardId: board.id, listIndex: 0, q: query } }"
|
||||
>
|
||||
{{ board.name }}
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
{{ board.name }}
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<b-dropdown
|
||||
v-if="activeBoardList"
|
||||
split
|
||||
variant="light"
|
||||
:split-to="{ name: 'board', params: { id: boardId } }"
|
||||
:text="activeBoardList.name"
|
||||
<b-dropdown
|
||||
v-if="activeBoardList"
|
||||
split
|
||||
variant="light"
|
||||
size="sm"
|
||||
class="ml-2"
|
||||
:split-to="{ name: 'board', params: { id: boardId } }"
|
||||
:text="activeBoardList.name"
|
||||
>
|
||||
<b-dropdown-item
|
||||
v-for="(list, listIndex) in activeBoard.lists"
|
||||
:key="list.id"
|
||||
:to="{ name: 'search', query: { boardId: activeBoard.id, listIndex, q: query } }"
|
||||
>
|
||||
<b-dropdown-item
|
||||
v-for="(list, listIndex) in activeBoard.lists"
|
||||
:key="list.id"
|
||||
:to="{ name: 'search', query: { boardId: activeBoard.id, listIndex, q: query } }"
|
||||
>
|
||||
{{ list.name }}
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
</b-button-group>
|
||||
{{ list.name }}
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<b-button :to="{ name: 'search' }" class="ml-auto" variant="light">
|
||||
<i class="fas fa-times fa-fw" aria-hidden />
|
||||
|
@ -104,6 +111,10 @@
|
|||
>
|
||||
<game-card-search :game="game" />
|
||||
</b-col>
|
||||
|
||||
<b-button @click="loadMoreResults">
|
||||
load more
|
||||
</b-button>
|
||||
</b-form-row>
|
||||
|
||||
<div
|
||||
|
@ -207,10 +218,10 @@ export default {
|
|||
},
|
||||
|
||||
async mounted() {
|
||||
if (this.showEmptyState) {
|
||||
} else {
|
||||
this.search();
|
||||
}
|
||||
this.search();
|
||||
// if (this.showEmptyState) {
|
||||
// } else {
|
||||
// }
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -221,7 +232,10 @@ export default {
|
|||
? `search "${this.query}";`
|
||||
: '';
|
||||
|
||||
const data = `${search} fields platforms,slug,cover.image_id; limit 50;`;
|
||||
const filter = !this.query
|
||||
? 'where rating >= 80;'
|
||||
: '';
|
||||
const data = `${search} fields platforms,slug,rating,cover.image_id; limit 50; ${filter}`;
|
||||
|
||||
this.searchResults = await this.$store.dispatch('IGDB', { path: 'games', data });
|
||||
|
||||
|
|
|
@ -3,7 +3,15 @@
|
|||
<b-container>
|
||||
<portal to="pageTitle">Account</portal>
|
||||
|
||||
<p>Your account</p>
|
||||
<p>Logged in as {{ user.displayName }} / {{ user.email }}</p>
|
||||
|
||||
<b-alert show class="field" variant="light">
|
||||
<small>
|
||||
<strong>User ID:</strong>
|
||||
{{ user.uid }}
|
||||
</small>
|
||||
</b-alert>
|
||||
|
||||
<b-button
|
||||
variant="light"
|
||||
@click="session_signOut"
|
||||
|
@ -11,10 +19,10 @@
|
|||
Log out
|
||||
</b-button>
|
||||
|
||||
<p class="mt-2">Delete account</p>
|
||||
<hr />
|
||||
|
||||
<b-button
|
||||
variant="warning"
|
||||
variant="outline-dark"
|
||||
@click="$bvModal.show('deleteAccount');"
|
||||
>
|
||||
Delete account
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
<section>
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col cols="8">
|
||||
<div class="d-flex align-items-center justify-content-between mb-2">
|
||||
Boards
|
||||
<b-col>
|
||||
<div class="d-flex align-items-center justify-content-between mb-2 pt-2">
|
||||
Recent boards
|
||||
|
||||
<b-button
|
||||
v-if="boards.length > 10"
|
||||
v-if="sortedBoards.length > 10"
|
||||
size="sm"
|
||||
title="Boards"
|
||||
variant="link"
|
||||
:to="{ name: 'boards' }"
|
||||
>
|
||||
View all boards
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
<b-row>
|
||||
<b-form-row>
|
||||
<b-col
|
||||
v-for="board in recentBoards"
|
||||
:key="board.id"
|
||||
|
@ -25,44 +24,20 @@
|
|||
sm="6"
|
||||
md="4"
|
||||
lg="3"
|
||||
class="mb-4"
|
||||
>
|
||||
<mini-board
|
||||
:board="board"
|
||||
style="height: 140px"
|
||||
class="mb-3"
|
||||
style="height: 180px"
|
||||
@click.native="$router.push({ name: 'board', params: { id: board.id } })"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-col>
|
||||
|
||||
<b-col cols="3 offset-1" class="text-center">
|
||||
<b-avatar
|
||||
class="d-flex ml-auto mr-auto mb-3 mt-5"
|
||||
rounded
|
||||
:src="avatarImage"
|
||||
size="140px"
|
||||
/>
|
||||
|
||||
<router-link
|
||||
v-if="profile.userName"
|
||||
:to="{ name: 'public.profile', params: { userName: profile.userName } }"
|
||||
>
|
||||
@{{ profile.userName }}
|
||||
</router-link>
|
||||
|
||||
<b-button
|
||||
v-else
|
||||
:to="{ name: 'profile.settings' }"
|
||||
variant="success"
|
||||
>
|
||||
Create profile <b-badge>New!</b-badge>
|
||||
</b-button>
|
||||
</b-form-row>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row class="mt-3">
|
||||
<b-col cols="6" md="3">
|
||||
<b-col cols="12" sm="6" md="3">
|
||||
<settings-card
|
||||
title="Wallpapers"
|
||||
description="Manage your wallpapers"
|
||||
|
@ -70,7 +45,7 @@
|
|||
@click.native="$router.push({ name: 'wallpapers' })"
|
||||
/>
|
||||
</b-col>
|
||||
<b-col cols="6" md="3">
|
||||
<b-col cols="12" sm="6" md="3">
|
||||
<settings-card
|
||||
title="Notes"
|
||||
description="View all your notes"
|
||||
|
@ -79,22 +54,65 @@
|
|||
/>
|
||||
</b-col>
|
||||
|
||||
<b-col cols="6" md="3">
|
||||
<!-- <b-col cols="12" sm="6" md="3">
|
||||
<b-card>
|
||||
<h4>Tags</h4>
|
||||
|
||||
<b-button
|
||||
v-for="({ textColor, bgColor, name, games }, index) in tags"
|
||||
@click="$router.push({ name: 'tag.edit', params: { id: index } })"
|
||||
rounded
|
||||
size="sm"
|
||||
class="mr-1 mb-1"
|
||||
variant="transparent"
|
||||
:style="`background-color: ${bgColor}; color: ${textColor}`"
|
||||
:key="name"
|
||||
>
|
||||
{{ name }} {{ games.length ? `(${games.length})` : '' }}
|
||||
</b-button>
|
||||
</b-card>
|
||||
</b-col> -->
|
||||
|
||||
|
||||
<b-col cols="12" sm="6" md="3">
|
||||
<settings-card
|
||||
title="Tags"
|
||||
description="View all your tags"
|
||||
description="Manage your tags"
|
||||
icon="fa-tags"
|
||||
@click.native="$router.push({ name: 'tags' })"
|
||||
/>
|
||||
</b-col>
|
||||
|
||||
<b-col cols="6" md="3">
|
||||
<b-col cols="12" sm="6" md="3">
|
||||
<settings-card
|
||||
title="Account"
|
||||
description="Manage your account"
|
||||
icon="fa-user"
|
||||
@click.native="$router.push({ name: 'account' })"
|
||||
/>
|
||||
|
||||
<b-avatar
|
||||
class="d-flex ml-auto mr-auto mb-3 mt-5"
|
||||
rounded
|
||||
:src="avatarImage"
|
||||
size="120px"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
v-if="profile.userName"
|
||||
variant="secondary"
|
||||
:to="{ name: 'public.profile', params: { userName: profile.userName } }"
|
||||
>
|
||||
@{{ profile.userName }}
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
v-else
|
||||
:to="{ name: 'profile.settings' }"
|
||||
variant="success"
|
||||
>
|
||||
Create profile <b-badge>New!</b-badge>
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
|
@ -178,7 +196,7 @@ import MiniBoard from '@/components/Board/MiniBoard';
|
|||
// import SteamSettingsPage from '@/pages/SteamSettingsPage';
|
||||
// import LanguageSettings from '@/components/Settings/LanguageSettings';
|
||||
import { getImageThumbnail } from '@/utils';
|
||||
import { mapState } from 'vuex';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -192,7 +210,9 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
popularGames: [115, 125174],
|
||||
profile: {},
|
||||
recentBoards: [],
|
||||
avatarImage: null,
|
||||
}
|
||||
},
|
||||
|
@ -216,11 +236,8 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['user', 'releases', 'boards']),
|
||||
|
||||
recentBoards() {
|
||||
return this.boards.slice(0, 8);
|
||||
},
|
||||
...mapState(['user', 'releases', 'tags']),
|
||||
...mapGetters(['sortedBoards']),
|
||||
|
||||
latestRelease() {
|
||||
return this.releases?.[0]?.tag_name;
|
||||
|
@ -229,6 +246,11 @@ export default {
|
|||
|
||||
methods: {
|
||||
async load() {
|
||||
await this.$store.dispatch('LOAD_BOARDS');
|
||||
await this.$store.dispatch('LOAD_TAGS');
|
||||
|
||||
this.recentBoards = this.sortedBoards.slice(0, 8);
|
||||
|
||||
this.profile = await this.$store.dispatch('LOAD_PROFILE').catch(() => null);
|
||||
if (this.profile?.avatar) this.loadAvatarImage();
|
||||
},
|
||||
|
|
|
@ -41,7 +41,6 @@ export default {
|
|||
return new Promise((resolve, reject) => {
|
||||
axios.get(`${API_BASE}/platforms?token=${state.twitchToken.access_token}`)
|
||||
.then(({ data }) => {
|
||||
commit('SET_PLATFORMS', data);
|
||||
resolve(data);
|
||||
}).catch(reject);
|
||||
});
|
||||
|
@ -120,8 +119,9 @@ export default {
|
|||
const board = doc.data();
|
||||
|
||||
return {
|
||||
id: doc.id,
|
||||
...board,
|
||||
id: doc.id,
|
||||
lastUpdated: board?.lastUpdated || 0,
|
||||
};
|
||||
})
|
||||
: null;
|
||||
|
|
|
@ -6,7 +6,7 @@ import presetHTML5 from '@bbob/preset-html5'
|
|||
import orderby from 'lodash.orderby';
|
||||
|
||||
export default {
|
||||
sortedBoards: ({ boards }) => orderby(boards, 'name'),
|
||||
sortedBoards: ({ boards }) => orderby(boards, 'lastUpdated', 'desc'),
|
||||
|
||||
isBoardOwner: ({ board, user }) => {
|
||||
return board?.owner === user?.uid;
|
||||
|
|
|
@ -2,7 +2,6 @@ import Vue from 'vue';
|
|||
// import {
|
||||
// // PLATFORM_CATEGORIES,
|
||||
// // EXCLUDED_PLATFORMS,
|
||||
// // PLATFORM_BG_HEX,
|
||||
// // PLATFORM_LOGO_FORMAT,
|
||||
// // PLATFORM_NAME_OVERRIDES,
|
||||
// // POPULAR_PLATFORMS,
|
||||
|
@ -65,28 +64,6 @@ export default {
|
|||
state.profiles = profiles;
|
||||
},
|
||||
|
||||
SET_PLATFORMS(state, platforms) {
|
||||
// TODO: use getter instead to get fresh data right away instead of once per session
|
||||
state.platforms = platforms;
|
||||
// state.platforms = platforms
|
||||
// .filter(({ id }) => !EXCLUDED_PLATFORMS.includes(id))
|
||||
// .map((platform) => {
|
||||
// const formattedPlatform = {
|
||||
// id: platform.id,
|
||||
// name: PLATFORM_NAME_OVERRIDES[platform.id] || platform.name,
|
||||
// slug: platform.slug,
|
||||
// category: PLATFORM_CATEGORIES[platform.category],
|
||||
// popular: POPULAR_PLATFORMS.includes(platform.id),
|
||||
// // categoryId: platform.category,
|
||||
// generation: platform.generation || 0,
|
||||
// bgHex: PLATFORM_BG_HEX[platform.id] || null,
|
||||
// logoFormat: PLATFORM_LOGO_FORMAT[platform.id] || 'svg',
|
||||
// };
|
||||
//
|
||||
// return formattedPlatform;
|
||||
// });
|
||||
},
|
||||
|
||||
SET_BOARD_GAMES(state, boardGames) {
|
||||
state.boardGames = boardGames;
|
||||
},
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Colors
|
||||
$primary: #3d6692 !default;
|
||||
$muted: #7d7f8c !default;
|
||||
$danger: #C0392B !default;
|
||||
|
@ -6,19 +5,10 @@ $warning: #F39C12 !default;
|
|||
$success: #23cd69 !default;
|
||||
$white: #f8f8ff !default;
|
||||
$black: #222222 !default;
|
||||
$info: #0071bc !default;
|
||||
$info: #3C5275 !default;
|
||||
$light: #dee4e7 !default;
|
||||
$dark: #37474f !default;
|
||||
$secondary: #b0bec5 !default;
|
||||
$secondary: #6D6969 !default;
|
||||
|
||||
$theme-colors: () !default;
|
||||
$theme-colors: map-merge((
|
||||
"primary": $primary,
|
||||
"secondary": $secondary,
|
||||
"success": $success,
|
||||
"info": $info,
|
||||
"warning": $warning,
|
||||
"danger": $danger,
|
||||
"light": $light,
|
||||
"dark": $dark,
|
||||
), $theme-colors);
|
||||
$theme-colors: map-merge(( "primary": $primary, "secondary": $secondary, "success": $success, "info": $info, "warning": $warning, "danger": $danger, "light": $light, "dark": $dark, ), $theme-colors);
|
||||
|
|
|
@ -26,8 +26,9 @@ $modal-header-border-width: 0;
|
|||
$modal-footer-border-width: 0 !default;
|
||||
$modal-footer-padding: 0 !default;
|
||||
$modal-header-padding: 1rem 1rem 0 !default;
|
||||
//
|
||||
// $modal-xl: 1140px !default;
|
||||
|
||||
// TODO: make xl modal take most of the screen
|
||||
// $modal-xl: 100vw !default;
|
||||
// $modal-lg: 800px !default;
|
||||
$modal-md: 600px !default;
|
||||
$modal-sm: 400px !default;
|
||||
|
|
Loading…
Reference in a new issue