mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.4.2
* Changed: Use the new Twitch Data module for fetching stream up-time when it isn't known, rather than forcing queries to re-fetch. * Changed: Add a method to Twitch Data for looking up stream up-time. * Fixed: The autocompletion component should not swallow key-presses when modifier keys are being held. * Fixed: Issue when comparing against `null` with `deep_equals`.
This commit is contained in:
parent
275248ca36
commit
80148e5579
16 changed files with 363 additions and 84 deletions
Binary file not shown.
|
@ -114,6 +114,10 @@
|
||||||
|
|
||||||
<glyph glyph-name="github" unicode="" d="M429 779q116 0 215-58t156-156 57-215q0-140-82-252t-211-155q-15-3-22 4t-7 17q0 1 0 43t0 75q0 54-29 79 32 3 57 10t53 22 45 37 30 58 11 84q0 67-44 115 21 51-4 114-16 5-46-6t-51-25l-21-13q-52 15-107 15t-108-15q-8 6-23 15t-47 22-47 7q-25-63-5-114-44-48-44-115 0-47 12-83t29-59 45-37 52-22 57-10q-21-20-27-58-12-5-25-8t-32-3-36 12-31 35q-11 18-27 29t-28 14l-11 1q-12 0-16-2t-3-7 5-8 7-6l4-3q12-6 24-21t18-29l6-13q7-21 24-34t37-17 39-3 31 1l13 3q0-22 0-50t1-30q0-10-8-17t-22-4q-129 43-211 155t-82 252q0 117 58 215t155 156 216 58z m-267-616q2 4-3 7-6 1-8-1-1-4 4-7 5-3 7 1z m18-19q4 3-1 9-6 5-9 2-4-3 1-9 5-6 9-2z m16-25q6 4 0 11-4 7-9 3-5-3 0-10t9-4z m24-23q4 4-2 10-7 7-11 2-5-5 2-11 6-6 11-1z m32-14q1 6-8 9-8 2-10-4t7-9q8-3 11 4z m35-3q0 7-10 6-9 0-9-6 0-7 10-6 9 0 9 6z m32 5q-1 7-10 5-9-1-8-8t10-4 8 7z" horiz-adv-x="857.1" />
|
<glyph glyph-name="github" unicode="" d="M429 779q116 0 215-58t156-156 57-215q0-140-82-252t-211-155q-15-3-22 4t-7 17q0 1 0 43t0 75q0 54-29 79 32 3 57 10t53 22 45 37 30 58 11 84q0 67-44 115 21 51-4 114-16 5-46-6t-51-25l-21-13q-52 15-107 15t-108-15q-8 6-23 15t-47 22-47 7q-25-63-5-114-44-48-44-115 0-47 12-83t29-59 45-37 52-22 57-10q-21-20-27-58-12-5-25-8t-32-3-36 12-31 35q-11 18-27 29t-28 14l-11 1q-12 0-16-2t-3-7 5-8 7-6l4-3q12-6 24-21t18-29l6-13q7-21 24-34t37-17 39-3 31 1l13 3q0-22 0-50t1-30q0-10-8-17t-22-4q-129 43-211 155t-82 252q0 117 58 215t155 156 216 58z m-267-616q2 4-3 7-6 1-8-1-1-4 4-7 5-3 7 1z m18-19q4 3-1 9-6 5-9 2-4-3 1-9 5-6 9-2z m16-25q6 4 0 11-4 7-9 3-5-3 0-10t9-4z m24-23q4 4-2 10-7 7-11 2-5-5 2-11 6-6 11-1z m32-14q1 6-8 9-8 2-10-4t7-9q8-3 11 4z m35-3q0 7-10 6-9 0-9-6 0-7 10-6 9 0 9 6z m32 5q-1 7-10 5-9-1-8-8t10-4 8 7z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="sort-down" unicode="" d="M571 243q0-15-10-25l-250-250q-11-11-25-11t-25 11l-250 250q-11 10-11 25t11 25 25 11h500q14 0 25-11t10-25z" horiz-adv-x="571.4" />
|
||||||
|
|
||||||
|
<glyph glyph-name="sort-up" unicode="" d="M571 457q0-14-10-25t-25-11h-500q-15 0-25 11t-11 25 11 25l250 250q10 11 25 11t25-11l250-250q10-10 10-25z" horiz-adv-x="571.4" />
|
||||||
|
|
||||||
<glyph glyph-name="gauge" unicode="" d="M214 207q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
<glyph glyph-name="gauge" unicode="" d="M214 207q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="download-cloud" unicode="" d="M714 332q0 8-5 13t-13 5h-125v196q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-196h-125q-8 0-13-5t-5-13q0-8 5-13l196-196q5-5 13-5t13 5l196 196q5 6 5 13z m357-125q0-89-62-151t-152-63h-607q-103 0-177 73t-73 177q0 72 39 134t105 92q-1 17-1 24 0 118 84 202t202 84q87 0 159-49t105-129q40 35 93 35 59 0 101-42t42-101q0-43-23-77 72-17 119-76t46-133z" horiz-adv-x="1071.4" />
|
<glyph glyph-name="download-cloud" unicode="" d="M714 332q0 8-5 13t-13 5h-125v196q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-196h-125q-8 0-13-5t-5-13q0-8 5-13l196-196q5-5 13-5t13 5l196 196q5 6 5 13z m357-125q0-89-62-151t-152-63h-607q-103 0-177 73t-73 177q0 72 39 134t105 92q-1 17-1 24 0 118 84 202t202 84q87 0 159-49t105-129q40 35 93 35 59 0 101-42t42-101q0-43-23-77 72-17 119-76t46-133z" horiz-adv-x="1071.4" />
|
||||||
|
@ -128,6 +132,10 @@
|
||||||
|
|
||||||
<glyph glyph-name="ellipsis-vert" unicode="" d="M214 154v-108q0-22-15-37t-38-16h-107q-23 0-38 16t-16 37v108q0 22 16 38t38 15h107q22 0 38-15t15-38z m0 285v-107q0-22-15-38t-38-15h-107q-23 0-38 15t-16 38v107q0 23 16 38t38 16h107q22 0 38-16t15-38z m0 286v-107q0-22-15-38t-38-16h-107q-23 0-38 16t-16 38v107q0 22 16 38t38 16h107q22 0 38-16t15-38z" horiz-adv-x="214.3" />
|
<glyph glyph-name="ellipsis-vert" unicode="" d="M214 154v-108q0-22-15-37t-38-16h-107q-23 0-38 16t-16 37v108q0 22 16 38t38 15h107q22 0 38-15t15-38z m0 285v-107q0-22-15-38t-38-15h-107q-23 0-38 15t-16 38v107q0 23 16 38t38 16h107q22 0 38-16t15-38z m0 286v-107q0-22-15-38t-38-16h-107q-23 0-38 16t-16 38v107q0 22 16 38t38 16h107q22 0 38-16t15-38z" horiz-adv-x="214.3" />
|
||||||
|
|
||||||
|
<glyph glyph-name="sort-alt-up" unicode="" d="M411 46q0-6-6-13l-178-178q-5-5-13-5-6 0-12 5l-179 179q-8 9-4 19 4 11 17 11h107v768q0 8 5 13t13 5h107q8 0 13-5t5-13v-768h107q8 0 13-5t5-13z m589-71v-107q0-8-5-13t-13-5h-464q-8 0-13 5t-5 13v107q0 8 5 13t13 5h464q8 0 13-5t5-13z m-107 286v-107q0-8-5-13t-13-5h-357q-8 0-13 5t-5 13v107q0 8 5 13t13 5h357q8 0 13-5t5-13z m-107 285v-107q0-7-5-12t-13-6h-250q-8 0-13 6t-5 12v107q0 8 5 13t13 5h250q8 0 13-5t5-13z m-107 286v-107q0-8-5-13t-13-5h-143q-8 0-13 5t-5 13v107q0 8 5 13t13 5h143q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="sort-alt-down" unicode="" d="M679-25v-107q0-8-5-13t-13-5h-143q-8 0-13 5t-5 13v107q0 8 5 13t13 5h143q8 0 13-5t5-13z m-268 71q0-6-6-13l-178-178q-5-5-13-5-6 0-12 5l-179 179q-8 9-4 19 4 11 17 11h107v768q0 8 5 13t13 5h107q8 0 13-5t5-13v-768h107q8 0 13-5t5-13z m375 215v-107q0-8-5-13t-13-5h-250q-8 0-13 5t-5 13v107q0 8 5 13t13 5h250q8 0 13-5t5-13z m107 285v-107q0-7-5-12t-13-6h-357q-8 0-13 6t-5 12v107q0 8 5 13t13 5h357q8 0 13-5t5-13z m107 286v-107q0-8-5-13t-13-5h-464q-8 0-13 5t-5 13v107q0 8 5 13t13 5h464q8 0 13-5t5-13z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="language" unicode="" d="M365 248q-1-1-7 1t-18 6l-11 5q-24 11-48 28-4 2-23 17t-21 16q-38-57-75-101-45-53-59-61-2-1-11-3t-10 0q3 3 46 52 12 13 48 64t43 66q10 17 29 55t20 43q-5 1-61-18-5-2-16-5t-19-5-10-3q-1-1-1-6t0-5q-3-5-18-8-12-4-26 0-10 2-15 11-3 4-3 13 3 1 13 3t17 3q32 9 59 18 55 20 56 20 6 1 24 11t25 12q5 1 12 4t8 3 3 0q2-7 0-18 0-2-7-16t-15-29-9-19q-14-28-43-73l35-16q7-3 42-18t38-15q2-1 5-14t3-18z m-114 272q1-9-3-16-6-13-28-21-16-7-33-7-15 2-27 15-8 8-10 23l0 1q2-1 11-3t15 0 32 9q20 7 31 8 9 0 12-9z m389-72l35-127-77 23z m-618-447l387 130v576l-387-130v-576z m692 177l57-17-101 367-56 17-120-299 57-18 25 62 118-36z m-280 537l319-103v212z m173-738l88-8-30-89-22 37q-73-46-154-60-32-7-51-7h-47q-44 0-111 22t-102 47q-5 4-5 9 0 5 3 8t7 3q2 0 10-5t17-9 12-6q40-21 89-34t87-14q54 0 94 8t87 28q9 4 17 9t19 11 16 9z m250 602v-602l-432 137q-8-3-209-71t-205-68q-8 0-10 7 0 1-1 2v602q2 5 2 5 3 3 11 6 60 20 84 28v214l311-110q1 0 90 31t176 60 90 30q11 0 11-12v-233z" horiz-adv-x="857.1" />
|
<glyph glyph-name="language" unicode="" d="M365 248q-1-1-7 1t-18 6l-11 5q-24 11-48 28-4 2-23 17t-21 16q-38-57-75-101-45-53-59-61-2-1-11-3t-10 0q3 3 46 52 12 13 48 64t43 66q10 17 29 55t20 43q-5 1-61-18-5-2-16-5t-19-5-10-3q-1-1-1-6t0-5q-3-5-18-8-12-4-26 0-10 2-15 11-3 4-3 13 3 1 13 3t17 3q32 9 59 18 55 20 56 20 6 1 24 11t25 12q5 1 12 4t8 3 3 0q2-7 0-18 0-2-7-16t-15-29-9-19q-14-28-43-73l35-16q7-3 42-18t38-15q2-1 5-14t3-18z m-114 272q1-9-3-16-6-13-28-21-16-7-33-7-15 2-27 15-8 8-10 23l0 1q2-1 11-3t15 0 32 9q20 7 31 8 9 0 12-9z m389-72l35-127-77 23z m-618-447l387 130v576l-387-130v-576z m692 177l57-17-101 367-56 17-120-299 57-18 25 62 118-36z m-280 537l319-103v212z m173-738l88-8-30-89-22 37q-73-46-154-60-32-7-51-7h-47q-44 0-111 22t-102 47q-5 4-5 9 0 5 3 8t7 3q2 0 10-5t17-9 12-6q40-21 89-34t87-14q54 0 94 8t87 28q9 4 17 9t19 11 16 9z m250 602v-602l-432 137q-8-3-209-71t-205-68q-8 0-10 7 0 1-1 2v602q2 5 2 5 3 3 11 6 60 20 84 28v214l311-110q1 0 90 31t176 60 90 30q11 0 11-12v-233z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<glyph glyph-name="twitch" unicode="" d="M500 608v-242h-81v242h81z m222 0v-242h-81v242h81z m0-424l141 141v444h-666v-585h182v-121l121 121h222z m222 666v-565l-242-242h-182l-121-122h-121v122h-222v646l61 161h827z" horiz-adv-x="1000" />
|
<glyph glyph-name="twitch" unicode="" d="M500 608v-242h-81v242h81z m222 0v-242h-81v242h81z m0-424l141 141v444h-666v-585h182v-121l121 121h222z m222 666v-565l-242-242h-182l-121-122h-121v122h-222v646l61 161h827z" horiz-adv-x="1000" />
|
||||||
|
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -151,7 +151,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 4, revision: 1,
|
major: 4, minor: 4, revision: 2,
|
||||||
commit: __git_commit__,
|
commit: __git_commit__,
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
|
|
|
@ -23,10 +23,10 @@ export default class BrowsePopular extends SiteModule {
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
// Popular Directory Channel Cards
|
// Popular Directory Channel Cards
|
||||||
this.apollo.ensureQuery(
|
/*this.apollo.ensureQuery(
|
||||||
'BrowsePage_Popular',
|
'BrowsePage_Popular',
|
||||||
'data.streams.edges.0.node.createdAt'
|
'data.streams.edges.0.node.createdAt'
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
modifyStreams(res) { // eslint-disable-line class-methods-use-this
|
modifyStreams(res) { // eslint-disable-line class-methods-use-this
|
||||||
|
|
|
@ -169,7 +169,7 @@ export default class Following extends SiteModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureQueries () {
|
ensureQueries () {
|
||||||
this.apollo.ensureQuery(
|
/*this.apollo.ensureQuery(
|
||||||
'FollowedChannels_RENAME2',
|
'FollowedChannels_RENAME2',
|
||||||
'data.currentUser.followedLiveUsers.nodes.0.stream.createdAt'
|
'data.currentUser.followedLiveUsers.nodes.0.stream.createdAt'
|
||||||
);
|
);
|
||||||
|
@ -182,7 +182,7 @@ export default class Following extends SiteModule {
|
||||||
this.apollo.ensureQuery(
|
this.apollo.ensureQuery(
|
||||||
'RecommendedChannels',
|
'RecommendedChannels',
|
||||||
'data.currentUser.recommendations.liveRecommendations.nodes.0.createdAt'
|
'data.currentUser.recommendations.liveRecommendations.nodes.0.createdAt'
|
||||||
);
|
);*/
|
||||||
|
|
||||||
if ( this.router.current_name !== 'dir-following' )
|
if ( this.router.current_name !== 'dir-following' )
|
||||||
return;
|
return;
|
||||||
|
@ -197,11 +197,11 @@ export default class Following extends SiteModule {
|
||||||
get('data.currentUser.followedHosts.nodes.0.hosting.stream.createdAt', n) !== undefined
|
get('data.currentUser.followedHosts.nodes.0.hosting.stream.createdAt', n) !== undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
else if ( bit === 'live' )
|
/*else if ( bit === 'live' )
|
||||||
this.apollo.ensureQuery(
|
this.apollo.ensureQuery(
|
||||||
'FollowingLive_CurrentUser',
|
'FollowingLive_CurrentUser',
|
||||||
'data.currentUser.followedLiveUsers.nodes.0.stream.createdAt'
|
'data.currentUser.followedLiveUsers.nodes.0.stream.createdAt'
|
||||||
);
|
);*/
|
||||||
|
|
||||||
else if ( bit === 'hosts' )
|
else if ( bit === 'hosts' )
|
||||||
this.apollo.ensureQuery(
|
this.apollo.ensureQuery(
|
||||||
|
@ -217,7 +217,7 @@ export default class Following extends SiteModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyHostMenu(event) {
|
destroyHostMenu(event) {
|
||||||
if (!event || event && event.target && event.target.closest('.ffz-channel-selector-outer') === null && Date.now() > this.hostMenuBuffer) {
|
if (!event || ! this.hostMenu || event && event.target && event.target.closest('.ffz-channel-selector-outer') === null && Date.now() > this.hostMenuBuffer) {
|
||||||
this.hostMenuPopper && this.hostMenuPopper.destroy();
|
this.hostMenuPopper && this.hostMenuPopper.destroy();
|
||||||
this.hostMenu && this.hostMenu.remove();
|
this.hostMenu && this.hostMenu.remove();
|
||||||
this.hostMenuPopper = this.hostMenu = undefined;
|
this.hostMenuPopper = this.hostMenu = undefined;
|
||||||
|
|
|
@ -29,11 +29,11 @@ export default class Game extends SiteModule {
|
||||||
|
|
||||||
this.apollo.registerModifier('DirectoryPage_Game', GAME_QUERY);
|
this.apollo.registerModifier('DirectoryPage_Game', GAME_QUERY);
|
||||||
this.apollo.registerModifier('DirectoryPage_Game', res => {
|
this.apollo.registerModifier('DirectoryPage_Game', res => {
|
||||||
setTimeout(() =>
|
/*setTimeout(() =>
|
||||||
this.apollo.ensureQuery(
|
this.apollo.ensureQuery(
|
||||||
'DirectoryPage_Game',
|
'DirectoryPage_Game',
|
||||||
'data.game.streams.edges.0.node.createdAt'
|
'data.game.streams.edges.0.node.createdAt'
|
||||||
), 500);
|
), 500);*/
|
||||||
|
|
||||||
this.modifyStreams(res);
|
this.modifyStreams(res);
|
||||||
}, false);
|
}, false);
|
||||||
|
@ -62,10 +62,10 @@ export default class Game extends SiteModule {
|
||||||
updateGameHeader(inst) {
|
updateGameHeader(inst) {
|
||||||
this.updateButtons(inst);
|
this.updateButtons(inst);
|
||||||
|
|
||||||
this.apollo.ensureQuery(
|
/*this.apollo.ensureQuery(
|
||||||
'DirectoryPage_Game',
|
'DirectoryPage_Game',
|
||||||
'data.game.streams.edges.0.node.createdAt'
|
'data.game.streams.edges.0.node.createdAt'
|
||||||
);
|
);*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ export default class Directory extends SiteModule {
|
||||||
this.inject('site.apollo');
|
this.inject('site.apollo');
|
||||||
this.inject('site.css_tweaks');
|
this.inject('site.css_tweaks');
|
||||||
this.inject('site.web_munch');
|
this.inject('site.web_munch');
|
||||||
|
this.inject('site.twitch_data');
|
||||||
|
|
||||||
this.inject('i18n');
|
this.inject('i18n');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
|
@ -200,7 +201,7 @@ export default class Directory extends SiteModule {
|
||||||
this.DirectoryVideos.forceUpdate();
|
this.DirectoryVideos.forceUpdate();
|
||||||
})
|
})
|
||||||
|
|
||||||
this.DirectoryCard.ready(cls => {
|
this.DirectoryCard.ready((cls, instances) => {
|
||||||
//const old_render = cls.prototype.render,
|
//const old_render = cls.prototype.render,
|
||||||
const old_render_iconic = cls.prototype.renderIconicImage,
|
const old_render_iconic = cls.prototype.renderIconicImage,
|
||||||
old_render_titles = cls.prototype.renderTitles;
|
old_render_titles = cls.prototype.renderTitles;
|
||||||
|
@ -272,13 +273,13 @@ export default class Directory extends SiteModule {
|
||||||
|
|
||||||
// Game Directory Channel Cards
|
// Game Directory Channel Cards
|
||||||
// TODO: Better query handling.
|
// TODO: Better query handling.
|
||||||
this.apollo.ensureQuery(
|
/*this.apollo.ensureQuery(
|
||||||
'DirectoryPage_Game',
|
'DirectoryPage_Game',
|
||||||
'data.game.streams.edges.0.node.createdAt'
|
'data.game.streams.edges.0.node.createdAt'
|
||||||
);
|
);*/
|
||||||
|
|
||||||
//for(const inst of instances)
|
for(const inst of instances)
|
||||||
// this.updateCard(inst);
|
this.updateCard(inst);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.DirectoryCard.on('update', this.updateCard, this);
|
this.DirectoryCard.on('update', this.updateCard, this);
|
||||||
|
@ -373,12 +374,33 @@ export default class Directory extends SiteModule {
|
||||||
updateUptime(inst, created_path) {
|
updateUptime(inst, created_path) {
|
||||||
const container = this.fine.getChildNode(inst),
|
const container = this.fine.getChildNode(inst),
|
||||||
card = container && container.querySelector && container.querySelector('.preview-card-overlay'),
|
card = container && container.querySelector && container.querySelector('.preview-card-overlay'),
|
||||||
setting = this.settings.get('directory.uptime'),
|
setting = this.settings.get('directory.uptime');
|
||||||
created_at = inst.props && inst.props.createdAt || get(created_path, inst),
|
|
||||||
up_since = created_at && new Date(created_at),
|
if ( ! card || setting === 0 || ! inst.props || inst.props.viewCount || inst.props.animatedImageProps )
|
||||||
|
return this.clearUptime(inst);
|
||||||
|
|
||||||
|
let created_at = inst.props.createdAt || get(created_path, inst);
|
||||||
|
|
||||||
|
if ( ! created_at ) {
|
||||||
|
if ( inst.ffz_stream_meta === undefined ) {
|
||||||
|
inst.ffz_stream_meta = null;
|
||||||
|
this.twitch_data.getStreamMeta(inst.props.channelId, inst.props.channelLogin).then(data => {
|
||||||
|
inst.ffz_stream_meta = data;
|
||||||
|
this.updateUptime(inst, created_path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( inst.ffz_stream_meta )
|
||||||
|
created_at = inst.ffz_stream_meta.createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! created_at )
|
||||||
|
return this.clearUptime(inst);
|
||||||
|
|
||||||
|
const up_since = created_at && new Date(created_at),
|
||||||
uptime = up_since && Math.floor((Date.now() - up_since) / 1000) || 0;
|
uptime = up_since && Math.floor((Date.now() - up_since) / 1000) || 0;
|
||||||
|
|
||||||
if ( ! card || setting === 0 || uptime < 1 )
|
if ( uptime < 1 )
|
||||||
return this.clearUptime(inst);
|
return this.clearUptime(inst);
|
||||||
|
|
||||||
const up_text = duration_to_string(uptime, false, false, false, setting === 1);
|
const up_text = duration_to_string(uptime, false, false, false, setting === 1);
|
||||||
|
|
|
@ -305,6 +305,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onHome(event) {
|
onHome(event) {
|
||||||
|
if ( event.ctrlKey || event.shiftKey || event.altKey )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! this.open )
|
if ( ! this.open )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -316,6 +319,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onEnd(event) {
|
onEnd(event) {
|
||||||
|
if ( event.ctrlKey || event.shiftKey || event.altKey )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! this.open )
|
if ( ! this.open )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -327,6 +333,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onUp(event) {
|
onUp(event) {
|
||||||
|
if ( event.ctrlKey || event.shiftKey || event.altKey )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! this.open )
|
if ( ! this.open )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -341,6 +350,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onDown(event) {
|
onDown(event) {
|
||||||
|
if ( event.ctrlKey || event.shiftKey || event.altKey )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! this.open )
|
if ( ! this.open )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -363,6 +375,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
onEnter(event) {
|
onEnter(event) {
|
||||||
|
if ( event.ctrlKey || event.shiftKey || event.altKey )
|
||||||
|
return;
|
||||||
|
|
||||||
if ( ! this.open )
|
if ( ! this.open )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
10
src/utilities/data/stream-fetch.gql
Normal file
10
src/utilities/data/stream-fetch.gql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
query FFZ_StreamFetch($ids: [ID!], $logins: [String!]) {
|
||||||
|
users(ids: $ids, logins: $logins) {
|
||||||
|
id
|
||||||
|
login
|
||||||
|
stream {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/utilities/data/stream-single.gql
Normal file
9
src/utilities/data/stream-single.gql
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
query FFZ_SingleStream($id: ID, $login: String) {
|
||||||
|
user(id: $id, login: $login) {
|
||||||
|
id
|
||||||
|
stream {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -179,6 +179,8 @@ export function deep_equals(object, other, ignore_undefined = false, seen, other
|
||||||
return false;
|
return false;
|
||||||
if ( typeof object !== 'object' )
|
if ( typeof object !== 'object' )
|
||||||
return false;
|
return false;
|
||||||
|
if ( (object === null) !== (other === null) )
|
||||||
|
return false;
|
||||||
|
|
||||||
if ( ! seen )
|
if ( ! seen )
|
||||||
seen = new Set;
|
seen = new Set;
|
||||||
|
|
|
@ -18,10 +18,14 @@ export default class TwitchData extends Module {
|
||||||
this.inject('site.apollo');
|
this.inject('site.apollo');
|
||||||
this.inject('site.web_munch');
|
this.inject('site.web_munch');
|
||||||
|
|
||||||
|
this._waiting_stream_ids = new Map;
|
||||||
|
this._waiting_stream_logins = new Map;
|
||||||
|
|
||||||
this.tag_cache = new Map;
|
this.tag_cache = new Map;
|
||||||
this._waiting_tags = new Map;
|
this._waiting_tags = new Map;
|
||||||
|
|
||||||
this._loadTags = debounce(this._loadTags.bind(this), 50);
|
this._loadTags = debounce(this._loadTags, 50);
|
||||||
|
this._loadStreams = debounce(this._loadStreams, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryApollo(query, variables, options) {
|
queryApollo(query, variables, options) {
|
||||||
|
@ -56,7 +60,7 @@ export default class TwitchData extends Module {
|
||||||
return this._search;
|
return this._search;
|
||||||
|
|
||||||
const apollo = this.apollo.client,
|
const apollo = this.apollo.client,
|
||||||
core = this.listeners.getCore(),
|
core = this.site.getCore(),
|
||||||
|
|
||||||
search_module = this.web_munch.getModule('algolia-search'),
|
search_module = this.web_munch.getModule('algolia-search'),
|
||||||
SearchClient = search_module && search_module.a;
|
SearchClient = search_module && search_module.a;
|
||||||
|
@ -121,55 +125,220 @@ export default class TwitchData extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Stream Up-Type (Uptime and Type, for Directory Purposes)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
getStreamMeta(id, login) {
|
||||||
|
return new Promise(async (s, f) => {
|
||||||
|
if ( id ) {
|
||||||
|
if ( this._waiting_stream_ids.has(id) )
|
||||||
|
this._waiting_stream_ids.get(id).push([s, f]);
|
||||||
|
else
|
||||||
|
this._waiting_stream_ids.set(id, [[s, f]]);
|
||||||
|
} else if ( login ) {
|
||||||
|
if ( this._waiting_stream_logins.has(login) )
|
||||||
|
this._waiting_stream_logins.get(login).push([s, f]);
|
||||||
|
else
|
||||||
|
this._waiting_stream_logins.set(login, [[s, f]]);
|
||||||
|
} else
|
||||||
|
f('id and login cannot both be null');
|
||||||
|
|
||||||
|
if ( ! this._loading_streams )
|
||||||
|
this._loadStreams();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async _loadStreams() {
|
||||||
|
if ( this._loading_streams )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._loading_streams = true;
|
||||||
|
|
||||||
|
// Get the first 50... things.
|
||||||
|
const ids = [...this._waiting_stream_ids.keys()].slice(0, 50),
|
||||||
|
remaining = 50 - ids.length,
|
||||||
|
logins = remaining > 0 ? [...this._waiting_stream_logins.keys()].slice(0, remaining) : [];
|
||||||
|
|
||||||
|
let nodes;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await this.queryApollo({
|
||||||
|
query: require('./data/stream-fetch.gql'),
|
||||||
|
variables: {
|
||||||
|
ids: ids.length ? ids : null,
|
||||||
|
logins: logins.length ? logins : null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nodes = get('data.users', data);
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
for(const id of ids) {
|
||||||
|
const promises = this._waiting_stream_ids.get(id);
|
||||||
|
this._waiting_stream_ids.delete(id);
|
||||||
|
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[1](err);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const login of logins) {
|
||||||
|
const promises = this._waiting_stream_logins.get(login);
|
||||||
|
this._waiting_stream_logins.delete(login);
|
||||||
|
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[1](err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id_set = new Set(ids),
|
||||||
|
login_set = new Set(logins);
|
||||||
|
|
||||||
|
if ( Array.isArray(nodes) )
|
||||||
|
for(const node of nodes) {
|
||||||
|
if ( ! node || ! node.id )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
id_set.delete(node.id);
|
||||||
|
login_set.delete(node.login);
|
||||||
|
|
||||||
|
let promises = this._waiting_stream_ids.get(node.id);
|
||||||
|
if ( promises ) {
|
||||||
|
this._waiting_stream_ids.delete(node.id);
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](node.stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
promises = this._waiting_stream_logins.get(node.login);
|
||||||
|
if ( promises ) {
|
||||||
|
this._waiting_stream_logins.delete(node.login);
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](node.stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const id of id_set) {
|
||||||
|
const promises = this._waiting_stream_ids.get(id);
|
||||||
|
if ( promises ) {
|
||||||
|
this._waiting_stream_ids.delete(id);
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const login of login_set) {
|
||||||
|
const promises = this._waiting_stream_logins.get(login);
|
||||||
|
if ( promises ) {
|
||||||
|
this._waiting_stream_logins.delete(login);
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loading_streams = false;
|
||||||
|
|
||||||
|
if ( this._waiting_stream_ids.size || this._waiting_stream_logins.size )
|
||||||
|
this._loadStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Tags
|
// Tags
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
|
memorizeTag(node, dispatch = true) {
|
||||||
|
// We want properly formed tags.
|
||||||
|
if ( ! node || ! node.id || ! node.tagName || ! node.localizedName )
|
||||||
|
return;
|
||||||
|
|
||||||
|
let old = null;
|
||||||
|
if ( this.tag_cache.has(node.id) )
|
||||||
|
old = this.tag_cache.get(old);
|
||||||
|
|
||||||
|
const match = node.isLanguageTag && LANGUAGE_MATCHER.exec(node.tagName),
|
||||||
|
lang = match && match[1] || null;
|
||||||
|
|
||||||
|
const new_tag = {
|
||||||
|
id: node.id,
|
||||||
|
value: node.id,
|
||||||
|
is_language: node.isLanguageTag,
|
||||||
|
language: lang,
|
||||||
|
name: node.tagName,
|
||||||
|
label: node.localizedName
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( node.localizedDescription )
|
||||||
|
new_tag.description = node.localizedDescription;
|
||||||
|
|
||||||
|
const tag = old ? Object.assign(old, new_tag) : new_tag;
|
||||||
|
this.tag_cache.set(tag.id, tag);
|
||||||
|
|
||||||
|
if ( dispatch && tag.description && this._waiting_tags.has(tag.id) ) {
|
||||||
|
const promises = this._waiting_tags.get(tag.id);
|
||||||
|
this._waiting_tags.delete(tag.id);
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
async _loadTags() {
|
async _loadTags() {
|
||||||
if ( this._loading_tags )
|
if ( this._loading_tags )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._loading_tags = true;
|
this._loading_tags = true;
|
||||||
const processing = this._waiting_tags;
|
|
||||||
this._waiting_tags = new Map;
|
// Get the first 50 tags.
|
||||||
|
const ids = [...this._waiting_tags.keys()].slice(0, 50);
|
||||||
|
|
||||||
|
let nodes
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
require('./data/tags-fetch.gql'),
|
require('./data/tags-fetch.gql'),
|
||||||
{
|
{
|
||||||
ids: [...processing.keys()]
|
ids
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const nodes = get('data.contentTags', data);
|
nodes = get('data.contentTags', data);
|
||||||
if ( Array.isArray(nodes) )
|
|
||||||
for(const node of nodes) {
|
|
||||||
const tag = {
|
|
||||||
id: node.id,
|
|
||||||
value: node.id,
|
|
||||||
is_language: node.isLanguageTag,
|
|
||||||
name: node.tagName,
|
|
||||||
label: node.localizedName,
|
|
||||||
description: node.localizedDescription
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tag_cache.set(tag.id, tag);
|
|
||||||
const promises = processing.get(tag.id);
|
|
||||||
if ( promises )
|
|
||||||
for(const pair of promises)
|
|
||||||
pair[0](tag);
|
|
||||||
|
|
||||||
promises.delete(tag.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const promises of processing.values())
|
|
||||||
for(const pair of promises)
|
|
||||||
pair[0](null);
|
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
for(const promises of processing.values())
|
for(const id of ids) {
|
||||||
|
const promises = this._waiting_tags.get(id);
|
||||||
|
this._waiting_tags.delete(id);
|
||||||
|
|
||||||
for(const pair of promises)
|
for(const pair of promises)
|
||||||
pair[1](err);
|
pair[1](err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id_set = new Set(ids);
|
||||||
|
|
||||||
|
if ( Array.isArray(nodes) )
|
||||||
|
for(const node of nodes) {
|
||||||
|
const tag = this.memorizeTag(node, false),
|
||||||
|
promises = this._waiting_tags.get(tag.id);
|
||||||
|
|
||||||
|
this._waiting_tags.delete(tag.id);
|
||||||
|
id_set.delete(tag.id);
|
||||||
|
|
||||||
|
if ( promises )
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const id of id_set) {
|
||||||
|
const promises = this._waiting_tags.get(id);
|
||||||
|
this._waiting_tags.delete(id);
|
||||||
|
|
||||||
|
for(const pair of promises)
|
||||||
|
pair[0](null);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loading_tags = false;
|
this._loading_tags = false;
|
||||||
|
@ -179,6 +348,10 @@ export default class TwitchData extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTag(id, want_description = false) {
|
getTag(id, want_description = false) {
|
||||||
|
// Make sure we weren't accidentally handed a tag object.
|
||||||
|
if ( id && id.id )
|
||||||
|
id = id.id;
|
||||||
|
|
||||||
if ( this.tag_cache.has(id) ) {
|
if ( this.tag_cache.has(id) ) {
|
||||||
const out = this.tag_cache.get(id);
|
const out = this.tag_cache.get(id);
|
||||||
if ( out && (out.description || ! want_description) )
|
if ( out && (out.description || ! want_description) )
|
||||||
|
@ -197,6 +370,10 @@ export default class TwitchData extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTagImmediate(id, callback, want_description = false) {
|
getTagImmediate(id, callback, want_description = false) {
|
||||||
|
// Make sure we weren't accidentally handed a tag object.
|
||||||
|
if ( id && id.id )
|
||||||
|
id = id.id;
|
||||||
|
|
||||||
let out = null;
|
let out = null;
|
||||||
if ( this.tag_cache.has(id) )
|
if ( this.tag_cache.has(id) )
|
||||||
out = this.tag_cache.get(id);
|
out = this.tag_cache.get(id);
|
||||||
|
@ -223,17 +400,7 @@ export default class TwitchData extends Module {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
seen.add(node.id);
|
seen.add(node.id);
|
||||||
const tag = {
|
out.push(this.memorizeTag(node));
|
||||||
id: node.id,
|
|
||||||
value: node.id,
|
|
||||||
is_language: node.isLanguageTag,
|
|
||||||
name: node.tagName,
|
|
||||||
label: node.localizedName,
|
|
||||||
description: node.localizedDescription
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tag_cache.set(tag.id, tag);
|
|
||||||
out.push(tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -258,46 +425,89 @@ export default class TwitchData extends Module {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMatchingTags(query, locale) {
|
async getMatchingTags(query, locale, category = null) {
|
||||||
if ( ! locale )
|
if ( ! locale )
|
||||||
locale = this.locale;
|
locale = this.locale;
|
||||||
|
|
||||||
const data = await this.searchClient.queryForType(
|
locale = locale.toLowerCase();
|
||||||
'tag', query, generateUUID(), {
|
|
||||||
hitsPerPage: 100,
|
|
||||||
facetFilters: [
|
|
||||||
|
|
||||||
],
|
let nodes;
|
||||||
restrictSearchableAttributes: [
|
|
||||||
`localizations.${locale}`,
|
if ( category ) {
|
||||||
'tag_name'
|
const data = await this.searchClient.queryForType(
|
||||||
]
|
'stream_tag', query, generateUUID(), {
|
||||||
}
|
hitsPerPage: 100,
|
||||||
);
|
faceFilters: [
|
||||||
|
`category_id:${category}`
|
||||||
|
],
|
||||||
|
restrictSearchableAttributes: [
|
||||||
|
`localizations.${locale}`,
|
||||||
|
'tag_name'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
nodes = get('streamTags.hits', data);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const data = await this.searchClient.queryForType(
|
||||||
|
'tag', query, generateUUID(), {
|
||||||
|
hitsPerPage: 100,
|
||||||
|
facetFilters: [
|
||||||
|
['tag_scope:SCOPE_ALL', 'tag_scope:SCOPE_CATEGORY']
|
||||||
|
],
|
||||||
|
restrictSearchableAttributes: [
|
||||||
|
`localizations.${locale}`,
|
||||||
|
'tag_name'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
nodes = get('tags.hits', data);
|
||||||
|
}
|
||||||
|
|
||||||
const nodes = get('streamTags.hits', data);
|
|
||||||
if ( ! Array.isArray(nodes) )
|
if ( ! Array.isArray(nodes) )
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
const out = [], seen = new Set;
|
const out = [], seen = new Set;
|
||||||
for(const node of nodes) {
|
for(const node of nodes) {
|
||||||
if ( ! node || seen.has(node.tag_id) )
|
const tag_id = node.tag_id || node.objectID;
|
||||||
|
if ( ! node || seen.has(tag_id) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
seen.add(node.tag_id);
|
seen.add(tag_id);
|
||||||
if ( ! this.tag_cache.has(node.tag_id) ) {
|
if ( ! this.tag_cache.has(tag_id) ) {
|
||||||
|
const match = node.tag_name && LANGUAGE_MATCHER.exec(node.tag_name),
|
||||||
|
lang = match && match[1] || null;
|
||||||
|
|
||||||
const tag = {
|
const tag = {
|
||||||
id: node.tag_id,
|
id: tag_id,
|
||||||
value: node.tag_id,
|
value: tag_id,
|
||||||
is_language: node.tag_name && LANGUAGE_MATCHER.test(node.tag_name),
|
is_language: lang != null,
|
||||||
|
language: lang,
|
||||||
label: node.localizations && (node.localizations[locale] || node.localizations['en-us']) || node.tag_name
|
label: node.localizations && (node.localizations[locale] || node.localizations['en-us']) || node.tag_name
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tag_cache.set(tag.id);
|
if ( node.description_localizations ) {
|
||||||
|
const desc = node.description_localizations[locale] || node.description_localizations['en-us'];
|
||||||
|
if ( desc )
|
||||||
|
tag.description = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tag_cache.set(tag.id, tag);
|
||||||
out.push(tag);
|
out.push(tag);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
out.push(this.tag_cache.get(node.tag_id));
|
const tag = this.tag_cache.get(tag_id);
|
||||||
|
if ( ! tag.description && node.description_localizations ) {
|
||||||
|
const desc = node.description_localizations[locale] || node.description_localizations['en-us'];
|
||||||
|
if ( desc ) {
|
||||||
|
tag.description = desc;
|
||||||
|
this.tag_cache.set(tag.id, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,8 @@
|
||||||
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
|
||||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||||
.ffz-i-github:before { content: '\f09b'; } /* '' */
|
.ffz-i-github:before { content: '\f09b'; } /* '' */
|
||||||
|
.ffz-i-sort-down:before { content: '\f0dd'; } /* '' */
|
||||||
|
.ffz-i-sort-up:before { content: '\f0de'; } /* '' */
|
||||||
.ffz-i-gauge:before { content: '\f0e4'; } /* '' */
|
.ffz-i-gauge:before { content: '\f0e4'; } /* '' */
|
||||||
.ffz-i-download-cloud:before { content: '\f0ed'; } /* '' */
|
.ffz-i-download-cloud:before { content: '\f0ed'; } /* '' */
|
||||||
.ffz-i-upload-cloud:before { content: '\f0ee'; } /* '' */
|
.ffz-i-upload-cloud:before { content: '\f0ee'; } /* '' */
|
||||||
|
@ -135,6 +137,8 @@
|
||||||
.ffz-i-keyboard:before { content: '\f11c'; } /* '' */
|
.ffz-i-keyboard:before { content: '\f11c'; } /* '' */
|
||||||
.ffz-i-calendar-empty:before { content: '\f133'; } /* '' */
|
.ffz-i-calendar-empty:before { content: '\f133'; } /* '' */
|
||||||
.ffz-i-ellipsis-vert:before { content: '\f142'; } /* '' */
|
.ffz-i-ellipsis-vert:before { content: '\f142'; } /* '' */
|
||||||
|
.ffz-i-sort-alt-up:before { content: '\f160'; } /* '' */
|
||||||
|
.ffz-i-sort-alt-down:before { content: '\f161'; } /* '' */
|
||||||
.ffz-i-language:before { content: '\f1ab'; } /* '' */
|
.ffz-i-language:before { content: '\f1ab'; } /* '' */
|
||||||
.ffz-i-twitch:before { content: '\f1e8'; } /* '' */
|
.ffz-i-twitch:before { content: '\f1e8'; } /* '' */
|
||||||
.ffz-i-bell-off:before { content: '\f1f7'; } /* '' */
|
.ffz-i-bell-off:before { content: '\f1f7'; } /* '' */
|
||||||
|
@ -146,7 +150,6 @@
|
||||||
.ffz-i-window-restore:before { content: '\f2d2'; } /* '' */
|
.ffz-i-window-restore:before { content: '\f2d2'; } /* '' */
|
||||||
.ffz-i-window-close:before { content: '\f2d3'; } /* '' */
|
.ffz-i-window-close:before { content: '\f2d3'; } /* '' */
|
||||||
|
|
||||||
|
|
||||||
.ffz-i-pd-1:before { margin-right: 1rem }
|
.ffz-i-pd-1:before { margin-right: 1rem }
|
||||||
.ffz-i-pd-2:before { margin-right: 2rem }
|
.ffz-i-pd-2:before { margin-right: 2rem }
|
||||||
.ffz-i-pd-3:before { margin-right: 3rem }
|
.ffz-i-pd-3:before { margin-right: 3rem }
|
Loading…
Add table
Add a link
Reference in a new issue