mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-22 22:30:52 +01:00
Merge branch 'dev' into 'relative-paths'
# Conflicts: # package.json
This commit is contained in:
commit
53e587d26c
86
.eslintrc.js
86
.eslintrc.js
@ -1,86 +0,0 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'react-app',
|
||||
'react-app/jest'
|
||||
],
|
||||
'rules': {
|
||||
'linebreak-style': [
|
||||
'off'
|
||||
],
|
||||
'quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'brace-style': [
|
||||
'error',
|
||||
'allman',
|
||||
{
|
||||
'allowSingleLine': true
|
||||
}
|
||||
],
|
||||
'object-curly-spacing': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'keyword-spacing': [
|
||||
'error',
|
||||
{
|
||||
'overrides': {
|
||||
'if': {
|
||||
'after': false
|
||||
},
|
||||
'for': {
|
||||
'after': false
|
||||
},
|
||||
'while': {
|
||||
'after': false
|
||||
},
|
||||
'switch': {
|
||||
'after': false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': [
|
||||
'off'
|
||||
],
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'off'
|
||||
],
|
||||
'@typescript-eslint/no-empty-function': [
|
||||
'error',
|
||||
{
|
||||
'allow': [
|
||||
'functions',
|
||||
'arrowFunctions',
|
||||
'generatorFunctions',
|
||||
'methods',
|
||||
'generatorMethods',
|
||||
'constructors'
|
||||
]
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'off'
|
||||
],
|
||||
'@typescript-eslint/ban-types': [
|
||||
'error',
|
||||
{
|
||||
'types': {
|
||||
'String': true,
|
||||
'Boolean': true,
|
||||
'Number': true,
|
||||
'Symbol': true,
|
||||
'{}': false,
|
||||
'Object': false,
|
||||
'object': false,
|
||||
'Function': false
|
||||
},
|
||||
'extendDefaults': true
|
||||
}
|
||||
],
|
||||
'no-switch-case-fall-through': [
|
||||
'off'
|
||||
]
|
||||
}
|
||||
}
|
67
.eslintrc.json
Normal file
67
.eslintrc.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"root": true,
|
||||
"settings": {
|
||||
"react": {
|
||||
"pragma": "React",
|
||||
"version": "18.0.0"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:react/recommended",
|
||||
"plugin:react/jsx-runtime",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"linebreak-style": [ "off" ],
|
||||
"quotes": [ "error", "single" ],
|
||||
"@typescript-eslint/indent": [ "error", 4, { "SwitchCase": 1 } ],
|
||||
"array-bracket-spacing": [ "error", "always" ],
|
||||
"brace-style": [ "error", "allman" ],
|
||||
"template-curly-spacing": [ "error", "always" ],
|
||||
"no-multi-spaces": [ "error" ],
|
||||
"jsx-quotes": [ "error" ],
|
||||
"react/prop-types": [ "off" ],
|
||||
"react/jsx-curly-spacing": [ "error", { "when": "always", "children": true } ],
|
||||
"react/jsx-equals-spacing": [ "error" ],
|
||||
"react/jsx-newline": [ "error", { "prevent": true } ],
|
||||
"@typescript-eslint/object-curly-spacing": [ "error", "always",
|
||||
{
|
||||
"arraysInObjects": true,
|
||||
"objectsInObjects": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
"types": {
|
||||
"String": true,
|
||||
"Boolean": true,
|
||||
"Number": true,
|
||||
"Symbol": true,
|
||||
"{}": false,
|
||||
"Object": false,
|
||||
"object": false,
|
||||
"Function": false
|
||||
},
|
||||
"extendDefaults": true
|
||||
}
|
||||
],
|
||||
"no-switch-case-fall-through": [ "off" ]
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
*.scss
|
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@ -4,15 +4,24 @@
|
||||
"typescript.preferences.quoteStyle": "single",
|
||||
"typescript.format.placeOpenBraceOnNewLineForControlBlocks": true,
|
||||
"typescript.format.placeOpenBraceOnNewLineForFunctions": true,
|
||||
"typescript.format.enable": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true,
|
||||
"source.fixAll.sortJSON": false,
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": true
|
||||
},
|
||||
"git.ignoreLimitWarning": true,
|
||||
"git.ignoreLimitWarning": true,
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
"editor.wordWrap": "on",
|
||||
"emmet.showExpandedAbbreviation": "never"
|
||||
"emmet.showExpandedAbbreviation": "never",
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"html",
|
||||
"typescriptreact"
|
||||
],
|
||||
"eslint.workingDirectories": [
|
||||
"./src"
|
||||
]
|
||||
}
|
||||
|
10
README.md
10
README.md
@ -1,11 +1,11 @@
|
||||
# Nitro React
|
||||
# Nitro React v2.1
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- You must have [git](https://git-scm.com/) installed
|
||||
- You must have [NodeJS](https://nodejs.org/) >= 16.13 installed
|
||||
- We recommend you use [Yarn](https://yarnpkg.com/) over [npm](https://npmjs.com/)
|
||||
- `npm i yarn -g`
|
||||
- [Git](https://git-scm.com/)
|
||||
- [NodeJS](https://nodejs.org/) >= 18
|
||||
- If using NodeJS < 18 remove `--openssl-legacy-provider` from the package.json scripts
|
||||
- [Yarn](https://yarnpkg.com/) `npm i yarn -g`
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -24,20 +24,25 @@ module.exports = {
|
||||
},
|
||||
module: {
|
||||
...webpackConfig.module,
|
||||
rules: webpackConfig.module.rules.map((rule) =>
|
||||
{
|
||||
if(!rule.oneOf) return rule;
|
||||
rules: [
|
||||
{
|
||||
test: /\.mjs$/,
|
||||
include: /node_modules/,
|
||||
type: 'javascript/auto'
|
||||
},
|
||||
...webpackConfig.module.rules.map((rule) => {
|
||||
if (!rule.oneOf) return rule;
|
||||
|
||||
return {
|
||||
...rule,
|
||||
oneOf: rule.oneOf.map((ruleObject) =>
|
||||
{
|
||||
if(!new RegExp(ruleObject.test).test('.ts') || !ruleObject.include) return ruleObject;
|
||||
|
||||
return { ...ruleObject, include: undefined };
|
||||
})
|
||||
};
|
||||
})
|
||||
return {
|
||||
...rule,
|
||||
oneOf: rule.oneOf.map((ruleObject) => {
|
||||
if (!new RegExp(ruleObject.test).test('.ts') || !ruleObject.include) return ruleObject;
|
||||
|
||||
return { ...ruleObject, include: undefined };
|
||||
})
|
||||
};
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
54
package.json
54
package.json
@ -1,47 +1,57 @@
|
||||
{
|
||||
"name": "nitro-react",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"homepage": ".",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "cross-env BROWSER=none IMAGE_INLINE_SIZE_LIMIT=100000 craco start",
|
||||
"build": "cross-env GENERATE_SOURCEMAP=false IMAGE_INLINE_SIZE_LIMIT=100000 craco build",
|
||||
"start": "cross-env SKIP_PREFLIGHT_CHECK=true BROWSER=none IMAGE_INLINE_SIZE_LIMIT=100000 craco --openssl-legacy-provider start",
|
||||
"build": "cross-env SKIP_PREFLIGHT_CHECK=true GENERATE_SOURCEMAP=false IMAGE_INLINE_SIZE_LIMIT=100000 craco --openssl-legacy-provider build",
|
||||
"build:prod": "npx browserslist@latest --update-db && yarn build",
|
||||
"test": "craco test",
|
||||
"eject": "react-scripts eject"
|
||||
"eject": "react-scripts eject",
|
||||
"eslint": "eslint src --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@craco/craco": "^6.3.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.17",
|
||||
"@nitrots/nitro-renderer": "^1.1.13",
|
||||
"@craco/craco": "^6.4.5",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@nitrots/nitro-renderer": "^1.3.4",
|
||||
"animate.css": "^4.1.1",
|
||||
"classnames": "^2.3.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"emoji-toolkit": "^6.6.0",
|
||||
"node-sass": "^6.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^2.0.0-alpha.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.2.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-slider": "^1.3.1",
|
||||
"react-slider": "^2.0.0",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"react-youtube": "^7.13.1",
|
||||
"typescript": "^4.3.5"
|
||||
"sass": "^1.53.0",
|
||||
"typescript": "^4.3.5",
|
||||
"use-between": "^1.3.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-error-overlay": "6.0.9"
|
||||
"react-error-overlay": "6.0.9",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.20.19",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/node": "^18.6.1",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-slider": "^1.3.1",
|
||||
"@types/react-transition-group": "^4.4.2",
|
||||
"@types/react-virtualized": "^9.21.13",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.1",
|
||||
"@types/react-transition-group": "^4.4.5",
|
||||
"@types/react-virtualized": "^9.21.21",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||
"@typescript-eslint/parser": "^5.30.7",
|
||||
"eslint": "^8.20.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"react-error-overlay": "6.0.9"
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,10 @@
|
||||
<script>
|
||||
const NitroConfig = {
|
||||
"config.urls": [ '/renderer-config.json', '/ui-config.json' ],
|
||||
"sso.ticket": (new URLSearchParams(window.location.search).get('sso') || null)
|
||||
"sso.ticket": (new URLSearchParams(window.location.search).get('sso') || null),
|
||||
"forward.type": (new URLSearchParams(window.location.search).get('room') ? 2 : -1),
|
||||
"forward.id": (new URLSearchParams(window.location.search).get('room') || 0),
|
||||
"friend.id": (new URLSearchParams(window.location.search).get('friend') || 0),
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"image.library.notifications.url": "${image.library.url}notifications/%image%.png",
|
||||
"achievements.images.url": "${image.library.url}Quests/%image%.png",
|
||||
"camera.url": "https://camera.com",
|
||||
"thumbnails.url": "https://camera.com/thumbnail/%thumbnail%.png",
|
||||
"camera.url": "https://camera.url",
|
||||
"thumbnails.url": "https://camera.url/thumbnail/%thumbnail%.png",
|
||||
"url.prefix": "https://website.com",
|
||||
"habbopages.url": "${url.prefix}/",
|
||||
"group.homepage.url": "${url.prefix}/groups/%groupid%/id",
|
||||
@ -13,23 +13,69 @@
|
||||
"user.badges.max.slots": 5,
|
||||
"camera.publish.disabled": false,
|
||||
"hc.disabled": false,
|
||||
"badge.descriptions.enabled": true,
|
||||
"motto.max.length": 38,
|
||||
"bot.name.max.length": 15,
|
||||
"navigator.room.models": [
|
||||
{ "clubLevel": 0, "tileSize": 104, "name": "a" },
|
||||
{ "clubLevel": 0, "tileSize": 94, "name": "b" },
|
||||
{ "clubLevel": 0, "tileSize": 36, "name": "c" },
|
||||
{ "clubLevel": 0, "tileSize": 84, "name": "d" },
|
||||
{ "clubLevel": 0, "tileSize": 80, "name": "e" },
|
||||
{ "clubLevel": 0, "tileSize": 80, "name": "f" },
|
||||
{ "clubLevel": 0, "tileSize": 416, "name": "i" },
|
||||
{ "clubLevel": 0, "tileSize": 320, "name": "j" },
|
||||
{ "clubLevel": 0, "tileSize": 448, "name": "k" },
|
||||
{ "clubLevel": 0, "tileSize": 352, "name": "l" },
|
||||
{ "clubLevel": 0, "tileSize": 384, "name": "m" },
|
||||
{ "clubLevel": 0, "tileSize": 372, "name": "n" },
|
||||
{ "clubLevel": 1, "tileSize": 80, "name": "g" },
|
||||
{ "clubLevel": 1, "tileSize": 74, "name": "h" },
|
||||
{ "clubLevel": 1, "tileSize": 416, "name": "o" },
|
||||
{ "clubLevel": 1, "tileSize": 352, "name": "p" },
|
||||
{ "clubLevel": 1, "tileSize": 304, "name": "q" },
|
||||
{ "clubLevel": 1, "tileSize": 336, "name": "r" },
|
||||
{ "clubLevel": 1, "tileSize": 748, "name": "u" },
|
||||
{ "clubLevel": 1, "tileSize": 438, "name": "v" },
|
||||
{ "clubLevel": 2, "tileSize": 540, "name": "t" },
|
||||
{ "clubLevel": 2, "tileSize": 512, "name": "w" },
|
||||
{ "clubLevel": 2, "tileSize": 396, "name": "x" },
|
||||
{ "clubLevel": 2, "tileSize": 440, "name": "y" },
|
||||
{ "clubLevel": 2, "tileSize": 456, "name": "z" },
|
||||
{ "clubLevel": 2, "tileSize": 208, "name": "0" },
|
||||
{ "clubLevel": 2, "tileSize": 1009, "name": "1" },
|
||||
{ "clubLevel": 2, "tileSize": 1044, "name": "2" },
|
||||
{ "clubLevel": 2, "tileSize": 183, "name": "3" },
|
||||
{ "clubLevel": 2, "tileSize": 254, "name": "4" },
|
||||
{ "clubLevel": 2, "tileSize": 1024, "name": "5" },
|
||||
{ "clubLevel": 2, "tileSize": 801, "name": "6" },
|
||||
{ "clubLevel": 2, "tileSize": 354, "name": "7" },
|
||||
{ "clubLevel": 2, "tileSize": 888, "name": "8" },
|
||||
{ "clubLevel": 2, "tileSize": 926, "name": "9" }
|
||||
],
|
||||
"hotelview": {
|
||||
"show.avatar": true,
|
||||
"widgets": {
|
||||
"slot.1.widget": "promoarticle",
|
||||
"slot.1.conf": "",
|
||||
"slot.1.conf": {},
|
||||
"slot.2.widget": "widgetcontainer",
|
||||
"slot.2.conf": "image:${image.library.url}web_promo_small/spromo_Canal_Bundle.png,texts:2021NitroPromo,btnLink:https://google.com",
|
||||
"slot.2.conf": {
|
||||
"image": "${image.library.url}web_promo_small/spromo_Canal_Bundle.png",
|
||||
"texts": "2021NitroPromo",
|
||||
"btnLink": "https://google.com"
|
||||
},
|
||||
"slot.3.widget": "promoarticle",
|
||||
"slot.3.conf": "",
|
||||
"slot.3.conf": {},
|
||||
"slot.4.widget": "",
|
||||
"slot.4.conf": "",
|
||||
"slot.4.conf": {},
|
||||
"slot.5.widget": "",
|
||||
"slot.5.conf": "",
|
||||
"slot.5.conf": {},
|
||||
"slot.6.widget": "achievementcompetition_hall_of_fame",
|
||||
"slot.6.conf": "",
|
||||
"slot.6.conf": {
|
||||
"campaign": "habboFameComp"
|
||||
},
|
||||
"slot.7.widget": "",
|
||||
"slot.7.conf": ""
|
||||
"slot.7.conf": {}
|
||||
},
|
||||
"images": {
|
||||
"background": "${asset.url}/images/reception/stretch_blue.png",
|
||||
@ -52,14 +98,18 @@
|
||||
0,
|
||||
5
|
||||
],
|
||||
"catalog.links": {
|
||||
"hc.buy_hc": "habbo_club",
|
||||
"hc.hc_gifts": "club_gifts",
|
||||
"pets.buy_food": "pet_food",
|
||||
"pets.buy_saddle": "saddles"
|
||||
},
|
||||
"hc.center": {
|
||||
"benefits.info": true,
|
||||
"payday.info": true,
|
||||
"gift.info": true,
|
||||
"benefits.habbopage": "habboclub",
|
||||
"payday.habbopage": "hcpayday",
|
||||
"catalog.buy": "habbo_club",
|
||||
"catalog.gifts": "club_gifts"
|
||||
"payday.habbopage": "hcpayday"
|
||||
},
|
||||
"respect.options": {
|
||||
"enabled": false,
|
||||
|
@ -19,7 +19,7 @@ $grid-active-border-color: $white;
|
||||
$toolbar-height: 55px;
|
||||
|
||||
$achievement-width: 375px;
|
||||
$achievement-height: 425px;
|
||||
$achievement-height: 405px;
|
||||
|
||||
$avatar-editor-width: 620px;
|
||||
$avatar-editor-height: 374px;
|
||||
@ -52,7 +52,7 @@ $friends-list-width: 250px;
|
||||
$friends-list-height: 300px;
|
||||
|
||||
$help-width: 450px;
|
||||
$help-height: 250px;
|
||||
$help-height: 290px;
|
||||
|
||||
$nitropedia-width: 400px;
|
||||
$nitropedia-height: 400px;
|
||||
|
134
src/App.tsx
134
src/App.tsx
@ -1,49 +1,42 @@
|
||||
import { ConfigurationEvent, HabboWebTools, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetCommunication, GetConfiguration, GetNitroInstance } from './api';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { DispatchUiEvent, GetCommunication, GetConfiguration, GetNitroInstance, GetUIVersion } from './api';
|
||||
import { Base, TransitionAnimation, TransitionAnimationTypes } from './common';
|
||||
import { LoadingView } from './components/loading/LoadingView';
|
||||
import { MainView } from './components/main/MainView';
|
||||
import { DispatchUiEvent, UseConfigurationEvent, UseLocalizationEvent, UseMainEvent, UseRoomEngineEvent } from './hooks';
|
||||
import { useConfigurationEvent, useLocalizationEvent, useMainEvent, useRoomEngineEvent } from './hooks';
|
||||
import IntervalWebWorker from './workers/IntervalWebWorker';
|
||||
import { WorkerBuilder } from './workers/WorkerBuilder';
|
||||
|
||||
NitroVersion.UI_VERSION = GetUIVersion();
|
||||
|
||||
export const App: FC<{}> = props =>
|
||||
{
|
||||
const [ isReady, setIsReady ] = useState(false);
|
||||
const [ isError, setIsError ] = useState(false);
|
||||
const [message, setMessage] = useState('Getting Ready');
|
||||
const [percent, setPercent] = useState(0);
|
||||
|
||||
//@ts-ignore
|
||||
if(!NitroConfig) throw new Error('NitroConfig is not defined!');
|
||||
const [ message, setMessage ] = useState('Getting Ready');
|
||||
const [ percent, setPercent ] = useState(0);
|
||||
const [ imageRendering, setImageRendering ] = useState<boolean>(true);
|
||||
|
||||
if(!GetNitroInstance())
|
||||
{
|
||||
NitroVersion.UI_VERSION = '2.0.0';
|
||||
//@ts-ignore
|
||||
if(!NitroConfig) throw new Error('NitroConfig is not defined!');
|
||||
|
||||
Nitro.bootstrap();
|
||||
|
||||
const worker = new WorkerBuilder(IntervalWebWorker);
|
||||
|
||||
Nitro.instance.setWorker(worker);
|
||||
}
|
||||
|
||||
const getPreloadAssetUrls = useCallback(() =>
|
||||
{
|
||||
const urls: string[] = [];
|
||||
const assetUrls = GetConfiguration<string[]>('preload.assets.urls');
|
||||
|
||||
if(assetUrls && assetUrls.length)
|
||||
{
|
||||
for(const url of assetUrls) urls.push(GetNitroInstance().core.configuration.interpolate(url));
|
||||
}
|
||||
|
||||
return urls;
|
||||
}, []);
|
||||
|
||||
const loadPercent = useCallback(() => setPercent(prevValue => (prevValue + 20)), []);
|
||||
|
||||
const handler = useCallback((event: NitroEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case ConfigurationEvent.LOADED:
|
||||
GetNitroInstance().localization.init();
|
||||
loadPercent();
|
||||
setPercent(prevValue => (prevValue + 20));
|
||||
return;
|
||||
case ConfigurationEvent.FAILED:
|
||||
setIsError(true);
|
||||
@ -60,44 +53,48 @@ export const App: FC<{}> = props =>
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_HANDSHAKING:
|
||||
loadPercent();
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_HANDSHAKE_FAILED:
|
||||
setPercent(prevValue => (prevValue + 20));
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_HANDSHAKE_FAILED:
|
||||
setIsError(true);
|
||||
setMessage('Handshake Failed');
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED:
|
||||
loadPercent();
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED:
|
||||
setPercent(prevValue => (prevValue + 20));
|
||||
|
||||
GetNitroInstance().init();
|
||||
|
||||
if(LegacyExternalInterface.available) LegacyExternalInterface.call('legacyTrack', 'authentication', 'authok', []);
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_ERROR:
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_ERROR:
|
||||
setIsError(true);
|
||||
setMessage('Connection Error');
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_CLOSED:
|
||||
return;
|
||||
case NitroCommunicationDemoEvent.CONNECTION_CLOSED:
|
||||
//if(GetNitroInstance().roomEngine) GetNitroInstance().roomEngine.dispose();
|
||||
|
||||
//setIsError(true);
|
||||
setMessage('Connection Error');
|
||||
|
||||
HabboWebTools.send(-1, 'client.init.handshake.fail');
|
||||
return;
|
||||
case RoomEngineEvent.ENGINE_INITIALIZED:
|
||||
loadPercent();
|
||||
setPercent(prevValue => (prevValue + 20));
|
||||
|
||||
setTimeout(() => setIsReady(true), 300);
|
||||
return;
|
||||
case NitroLocalizationEvent.LOADED:
|
||||
GetNitroInstance().core.asset.downloadAssets(getPreloadAssetUrls(), (status: boolean) =>
|
||||
case NitroLocalizationEvent.LOADED: {
|
||||
const assetUrls = GetConfiguration<string[]>('preload.assets.urls');
|
||||
const urls: string[] = [];
|
||||
|
||||
if(assetUrls && assetUrls.length) for(const url of assetUrls) urls.push(GetNitroInstance().core.configuration.interpolate(url));
|
||||
|
||||
GetNitroInstance().core.asset.downloadAssets(urls, (status: boolean) =>
|
||||
{
|
||||
if(status)
|
||||
{
|
||||
GetCommunication().init();
|
||||
|
||||
loadPercent();
|
||||
setPercent(prevValue => (prevValue + 20))
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -106,34 +103,49 @@ export const App: FC<{}> = props =>
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, [ getPreloadAssetUrls,loadPercent ]);
|
||||
}, []);
|
||||
|
||||
UseMainEvent(Nitro.WEBGL_UNAVAILABLE, handler);
|
||||
UseMainEvent(Nitro.WEBGL_CONTEXT_LOST, handler);
|
||||
UseMainEvent(NitroCommunicationDemoEvent.CONNECTION_HANDSHAKING, handler);
|
||||
UseMainEvent(NitroCommunicationDemoEvent.CONNECTION_HANDSHAKE_FAILED, handler);
|
||||
UseMainEvent(NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED, handler);
|
||||
UseMainEvent(NitroCommunicationDemoEvent.CONNECTION_ERROR, handler);
|
||||
UseMainEvent(NitroCommunicationDemoEvent.CONNECTION_CLOSED, handler);
|
||||
UseRoomEngineEvent(RoomEngineEvent.ENGINE_INITIALIZED, handler);
|
||||
UseLocalizationEvent(NitroLocalizationEvent.LOADED, handler);
|
||||
UseConfigurationEvent(ConfigurationEvent.LOADED, handler);
|
||||
UseConfigurationEvent(ConfigurationEvent.FAILED, handler);
|
||||
useMainEvent(Nitro.WEBGL_UNAVAILABLE, handler);
|
||||
useMainEvent(Nitro.WEBGL_CONTEXT_LOST, handler);
|
||||
useMainEvent(NitroCommunicationDemoEvent.CONNECTION_HANDSHAKING, handler);
|
||||
useMainEvent(NitroCommunicationDemoEvent.CONNECTION_HANDSHAKE_FAILED, handler);
|
||||
useMainEvent(NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED, handler);
|
||||
useMainEvent(NitroCommunicationDemoEvent.CONNECTION_ERROR, handler);
|
||||
useMainEvent(NitroCommunicationDemoEvent.CONNECTION_CLOSED, handler);
|
||||
useRoomEngineEvent(RoomEngineEvent.ENGINE_INITIALIZED, handler);
|
||||
useLocalizationEvent(NitroLocalizationEvent.LOADED, handler);
|
||||
useConfigurationEvent(ConfigurationEvent.LOADED, handler);
|
||||
useConfigurationEvent(ConfigurationEvent.FAILED, handler);
|
||||
|
||||
if(!WebGL.isWebGLAvailable())
|
||||
useEffect(() =>
|
||||
{
|
||||
DispatchUiEvent(new NitroEvent(Nitro.WEBGL_UNAVAILABLE));
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNitroInstance().core.configuration.init();
|
||||
}
|
||||
if(!WebGL.isWebGLAvailable())
|
||||
{
|
||||
DispatchUiEvent(new NitroEvent(Nitro.WEBGL_UNAVAILABLE));
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNitroInstance().core.configuration.init();
|
||||
}
|
||||
|
||||
const resize = (event: UIEvent) => setImageRendering(!(window.devicePixelRatio % 1));
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
resize(null);
|
||||
|
||||
return () =>
|
||||
{
|
||||
window.removeEventListener('resize', resize);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Base fit overflow="hidden">
|
||||
<Base fit overflow="hidden" className={ imageRendering && 'image-rendering-pixelated' }>
|
||||
{ (!isReady || isError) &&
|
||||
<LoadingView isError={isError} message={message} percent={ percent } /> }
|
||||
<LoadingView isError={ isError } message={ message } percent={ percent } /> }
|
||||
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ (isReady) }>
|
||||
<MainView />
|
||||
</TransitionAnimation>
|
||||
|
3
src/api/GetRendererVersion.ts
Normal file
3
src/api/GetRendererVersion.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { NitroVersion } from '@nitrots/nitro-renderer';
|
||||
|
||||
export const GetRendererVersion = () => NitroVersion.RENDERER_VERSION;
|
1
src/api/GetUIVersion.ts
Normal file
1
src/api/GetUIVersion.ts
Normal file
@ -0,0 +1 @@
|
||||
export const GetUIVersion = () => '2.1.0';
|
@ -1,6 +1,8 @@
|
||||
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||
import { AchievementUtilities } from './AchievementUtilities';
|
||||
import { IAchievementCategory } from './IAchievementCategory';
|
||||
|
||||
export class AchievementCategory
|
||||
export class AchievementCategory implements IAchievementCategory
|
||||
{
|
||||
private _code: string;
|
||||
private _achievements: AchievementData[];
|
||||
@ -13,26 +15,12 @@ export class AchievementCategory
|
||||
|
||||
public getProgress(): number
|
||||
{
|
||||
let progress = 0;
|
||||
|
||||
for(const achievement of this._achievements)
|
||||
{
|
||||
progress += (achievement.finalLevel ? achievement.level : (achievement.level - 1));
|
||||
}
|
||||
|
||||
return progress;
|
||||
return AchievementUtilities.getAchievementCategoryProgress(this);
|
||||
}
|
||||
|
||||
public getMaxProgress(): number
|
||||
{
|
||||
let progress = 0;
|
||||
|
||||
for(const achievement of this._achievements)
|
||||
{
|
||||
progress += achievement.levelCount;
|
||||
}
|
||||
|
||||
return progress;
|
||||
return AchievementUtilities.getAchievementCategoryMaxProgress(this);
|
||||
}
|
||||
|
||||
public get code(): string
|
97
src/api/achievements/AchievementUtilities.ts
Normal file
97
src/api/achievements/AchievementUtilities.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||
import { GetConfiguration, GetLocalization } from '../nitro';
|
||||
import { IAchievementCategory } from './IAchievementCategory';
|
||||
|
||||
export class AchievementUtilities
|
||||
{
|
||||
public static getAchievementBadgeCode(achievement: AchievementData): string
|
||||
{
|
||||
if(!achievement) return null;
|
||||
|
||||
let badgeId = achievement.badgeId;
|
||||
|
||||
if(!achievement.finalLevel) badgeId = GetLocalization().getPreviousLevelBadgeId(badgeId);
|
||||
|
||||
return badgeId;
|
||||
}
|
||||
|
||||
public static getAchievementCategoryImageUrl(category: IAchievementCategory, progress: number = null, icon: boolean = false): string
|
||||
{
|
||||
const imageUrl = GetConfiguration<string>('achievements.images.url');
|
||||
|
||||
let imageName = icon ? 'achicon_' : 'achcategory_';
|
||||
|
||||
imageName += category.code;
|
||||
|
||||
if(progress !== null) imageName += `_${ ((progress > 0) ? 'active' : 'inactive') }`;
|
||||
|
||||
return imageUrl.replace('%image%', imageName);
|
||||
}
|
||||
|
||||
public static getAchievementCategoryMaxProgress(category: IAchievementCategory): number
|
||||
{
|
||||
if(!category) return 0;
|
||||
|
||||
let progress = 0;
|
||||
|
||||
for(const achievement of category.achievements)
|
||||
{
|
||||
progress += achievement.levelCount;
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
public static getAchievementCategoryProgress(category: IAchievementCategory): number
|
||||
{
|
||||
if(!category) return 0;
|
||||
|
||||
let progress = 0;
|
||||
|
||||
for(const achievement of category.achievements) progress += (achievement.finalLevel ? achievement.level : (achievement.level - 1));
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
public static getAchievementCategoryTotalUnseen(category: IAchievementCategory): number
|
||||
{
|
||||
if(!category) return 0;
|
||||
|
||||
let unseen = 0;
|
||||
|
||||
for(const achievement of category.achievements) ((achievement.unseen > 0) && unseen++);
|
||||
|
||||
return unseen;
|
||||
}
|
||||
|
||||
public static getAchievementHasStarted(achievement: AchievementData): boolean
|
||||
{
|
||||
if(!achievement) return false;
|
||||
|
||||
if(achievement.finalLevel || ((achievement.level - 1) > 0)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static getAchievementIsIgnored(achievement: AchievementData): boolean
|
||||
{
|
||||
if(!achievement) return false;
|
||||
|
||||
const ignored = GetConfiguration<string[]>('achievements.unseen.ignored');
|
||||
const value = achievement.badgeId.replace(/[0-9]/g, '');
|
||||
const index = ignored.indexOf(value);
|
||||
|
||||
if(index >= 0) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static getAchievementLevel(achievement: AchievementData): number
|
||||
{
|
||||
if(!achievement) return 0;
|
||||
|
||||
if(achievement.finalLevel) return achievement.level;
|
||||
|
||||
return (achievement.level - 1);
|
||||
}
|
||||
}
|
7
src/api/achievements/IAchievementCategory.ts
Normal file
7
src/api/achievements/IAchievementCategory.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface IAchievementCategory
|
||||
{
|
||||
code: string;
|
||||
achievements: AchievementData[];
|
||||
}
|
3
src/api/achievements/index.ts
Normal file
3
src/api/achievements/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './AchievementCategory';
|
||||
export * from './AchievementUtilities';
|
||||
export * from './IAchievementCategory';
|
@ -1,12 +1,12 @@
|
||||
import { AvatarFigurePartType, IAvatarImageListener, IAvatarRenderManager, IFigurePart, IFigurePartSet, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
import { GetAvatarRenderManager } from '../nitro';
|
||||
import { FigureData } from './FigureData';
|
||||
|
||||
export class AvatarEditorGridPartItem implements IAvatarImageListener
|
||||
{
|
||||
private static ALPHA_FILTER: NitroAlphaFilter = new NitroAlphaFilter(0.2);
|
||||
private static THUMB_DIRECTIONS: number[] = [2, 6, 0, 4, 3, 1];
|
||||
private static DRAW_ORDER: string[] = [
|
||||
private static THUMB_DIRECTIONS: number[] = [ 2, 6, 0, 4, 3, 1 ];
|
||||
private static DRAW_ORDER: string[] = [
|
||||
AvatarFigurePartType.LEFT_HAND_ITEM,
|
||||
AvatarFigurePartType.LEFT_HAND,
|
||||
AvatarFigurePartType.LEFT_SLEEVE,
|
||||
@ -201,13 +201,13 @@ export class AvatarEditorGridPartItem implements IAvatarImageListener
|
||||
|
||||
if(this._partSet)
|
||||
{
|
||||
this._isHC = (this._partSet.clubLevel > 0);
|
||||
this._isSellable = this._partSet.isSellable;
|
||||
this._isHC = (this._partSet.clubLevel > 0);
|
||||
this._isSellable = this._partSet.isSellable;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._isHC = false;
|
||||
this._isSellable = false;
|
||||
this._isHC = false;
|
||||
this._isSellable = false;
|
||||
}
|
||||
|
||||
if(this._isDisabled) this.setAlpha(container, 0.2);
|
@ -1,5 +1,5 @@
|
||||
import { IPartColor } from '@nitrots/nitro-renderer';
|
||||
import { GetAvatarPalette, GetAvatarRenderManager, GetAvatarSetType, GetClubMemberLevel, GetConfiguration } from '../../../api';
|
||||
import { GetAvatarPalette, GetAvatarRenderManager, GetAvatarSetType, GetClubMemberLevel, GetConfiguration } from '../nitro';
|
||||
import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem';
|
||||
import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
@ -1,5 +1,5 @@
|
||||
import { AvatarEditorFigureCategory, AvatarScaleType, AvatarSetType } from '@nitrots/nitro-renderer';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
import { GetAvatarRenderManager } from '../nitro';
|
||||
import { AvatarEditorUtilities } from './AvatarEditorUtilities';
|
||||
import { CategoryBaseModel } from './CategoryBaseModel';
|
||||
import { FigureData } from './FigureData';
|
@ -11,7 +11,7 @@ export class CategoryBaseModel implements IAvatarEditorCategoryModel
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._isInitalized = false;
|
||||
this._isInitalized = false;
|
||||
this._maxPaletteCount = 0;
|
||||
}
|
||||
|
@ -199,8 +199,8 @@ export class FigureData
|
||||
{
|
||||
let figureString = '';
|
||||
|
||||
const setTypes: string[] = [ FigureData.FACE ];
|
||||
const figureSets: string[] = [];
|
||||
const setTypes: string[] = [ FigureData.FACE ];
|
||||
const figureSets: string[] = [];
|
||||
|
||||
for(const setType of setTypes)
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
import { AvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType } from '@nitrots/nitro-renderer';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
import { Randomizer } from '../../../api/utils';
|
||||
import { GetAvatarRenderManager } from '../nitro';
|
||||
import { Randomizer } from '../utils';
|
||||
import { FigureData } from './FigureData';
|
||||
|
||||
function getTotalColors(partSet: IFigurePartSet): number
|
||||
@ -26,11 +26,11 @@ function getRandomPartSet(setType: SetType, gender: string, clubLevel: number =
|
||||
if(!setType) return null;
|
||||
|
||||
const options = setType.partSets.getValues().filter(option =>
|
||||
{
|
||||
if(!option.isSelectable || ((option.gender !== 'U') && (option.gender !== gender)) || (option.clubLevel > clubLevel) || (option.isSellable && (figureSetIds.indexOf(option.id) === -1))) return null;
|
||||
{
|
||||
if(!option.isSelectable || ((option.gender !== 'U') && (option.gender !== gender)) || (option.clubLevel > clubLevel) || (option.isSellable && (figureSetIds.indexOf(option.id) === -1))) return null;
|
||||
|
||||
return option;
|
||||
});
|
||||
return option;
|
||||
});
|
||||
|
||||
if(!options || !options.length) return null;
|
||||
|
||||
@ -42,11 +42,11 @@ function getRandomColors(palette: IPalette, partSet: IFigurePartSet, clubLevel:
|
||||
if(!palette) return [];
|
||||
|
||||
const options = palette.colors.getValues().filter(option =>
|
||||
{
|
||||
if(!option.isSelectable || (option.clubLevel > clubLevel)) return null;
|
||||
{
|
||||
if(!option.isSelectable || (option.clubLevel > clubLevel)) return null;
|
||||
|
||||
return option;
|
||||
});
|
||||
return option;
|
||||
});
|
||||
|
||||
if(!options || !options.length) return null;
|
||||
|
13
src/api/avatar/index.ts
Normal file
13
src/api/avatar/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export * from './AvatarEditorAction';
|
||||
export * from './AvatarEditorGridColorItem';
|
||||
export * from './AvatarEditorGridPartItem';
|
||||
export * from './AvatarEditorUtilities';
|
||||
export * from './BodyModel';
|
||||
export * from './CategoryBaseModel';
|
||||
export * from './CategoryData';
|
||||
export * from './FigureData';
|
||||
export * from './FigureGenerator';
|
||||
export * from './HeadModel';
|
||||
export * from './IAvatarEditorCategoryModel';
|
||||
export * from './LegModel';
|
||||
export * from './TorsoModel';
|
@ -4,5 +4,6 @@ export class CameraPicture
|
||||
{
|
||||
constructor(
|
||||
public texture: NitroTexture,
|
||||
public imageUrl: string) {}
|
||||
public imageUrl: string)
|
||||
{}
|
||||
}
|
@ -2,5 +2,6 @@ export class CameraPictureThumbnail
|
||||
{
|
||||
constructor(
|
||||
public effectName: string,
|
||||
public thumbnailUrl: string) {}
|
||||
public thumbnailUrl: string)
|
||||
{}
|
||||
}
|
3
src/api/camera/index.ts
Normal file
3
src/api/camera/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './CameraEditorTabs';
|
||||
export * from './CameraPicture';
|
||||
export * from './CameraPictureThumbnail';
|
3
src/api/campaign/index.ts
Normal file
3
src/api/campaign/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './CalendarItem';
|
||||
export * from './CalendarItemState';
|
||||
export * from './ICalendarItem';
|
10
src/api/catalog/BuilderFurniPlaceableStatus.ts
Normal file
10
src/api/catalog/BuilderFurniPlaceableStatus.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export class BuilderFurniPlaceableStatus
|
||||
{
|
||||
public static OKAY: number = 0;
|
||||
public static MISSING_OFFER: number = 1;
|
||||
public static FURNI_LIMIT_REACHED: number = 2;
|
||||
public static NOT_IN_ROOM: number = 3;
|
||||
public static NOT_ROOM_OWNER: number = 4;
|
||||
public static GUILD_ROOM: number = 5;
|
||||
public static VISITORS_IN_ROOM: number = 6;
|
||||
}
|
@ -5,5 +5,6 @@ export class CatalogPetPalette
|
||||
constructor(
|
||||
public readonly breed: string,
|
||||
public readonly palettes: SellablePetPaletteData[]
|
||||
) {}
|
||||
)
|
||||
{}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { SellablePetPaletteData } from '@nitrots/nitro-renderer';
|
||||
import { GetRoomEngine } from '../../../api';
|
||||
import { GetRoomEngine } from '../nitro';
|
||||
import { ICatalogNode } from './ICatalogNode';
|
||||
|
||||
export const GetPixelEffectIcon = (id: number) =>
|
||||
@ -85,21 +85,21 @@ export function GetPetAvailableColors(petIndex: number, palettes: SellablePetPal
|
||||
switch(petIndex)
|
||||
{
|
||||
case 0:
|
||||
return [[16743226], [16750435], [16764339], [0xF59500], [16498012], [16704690], [0xEDD400], [16115545], [16513201], [8694111], [11585939], [14413767], [6664599], [9553845], [12971486], [8358322], [10002885], [13292268], [10780600], [12623573], [14403561], [12418717], [14327229], [15517403], [14515069], [15764368], [16366271], [0xABABAB], [0xD4D4D4], [0xFFFFFF], [14256481], [14656129], [15848130], [14005087], [14337152], [15918540], [15118118], [15531929], [9764857], [11258085]];
|
||||
return [ [ 16743226 ], [ 16750435 ], [ 16764339 ], [ 0xF59500 ], [ 16498012 ], [ 16704690 ], [ 0xEDD400 ], [ 16115545 ], [ 16513201 ], [ 8694111 ], [ 11585939 ], [ 14413767 ], [ 6664599 ], [ 9553845 ], [ 12971486 ], [ 8358322 ], [ 10002885 ], [ 13292268 ], [ 10780600 ], [ 12623573 ], [ 14403561 ], [ 12418717 ], [ 14327229 ], [ 15517403 ], [ 14515069 ], [ 15764368 ], [ 16366271 ], [ 0xABABAB ], [ 0xD4D4D4 ], [ 0xFFFFFF ], [ 14256481 ], [ 14656129 ], [ 15848130 ], [ 14005087 ], [ 14337152 ], [ 15918540 ], [ 15118118 ], [ 15531929 ], [ 9764857 ], [ 11258085 ] ];
|
||||
case 1:
|
||||
return [[16743226], [16750435], [16764339], [0xF59500], [16498012], [16704690], [0xEDD400], [16115545], [16513201], [8694111], [11585939], [14413767], [6664599], [9553845], [12971486], [8358322], [10002885], [13292268], [10780600], [12623573], [14403561], [12418717], [14327229], [15517403], [14515069], [15764368], [16366271], [0xABABAB], [0xD4D4D4], [0xFFFFFF], [14256481], [14656129], [15848130], [14005087], [14337152], [15918540], [15118118], [15531929], [9764857], [11258085]];
|
||||
return [ [ 16743226 ], [ 16750435 ], [ 16764339 ], [ 0xF59500 ], [ 16498012 ], [ 16704690 ], [ 0xEDD400 ], [ 16115545 ], [ 16513201 ], [ 8694111 ], [ 11585939 ], [ 14413767 ], [ 6664599 ], [ 9553845 ], [ 12971486 ], [ 8358322 ], [ 10002885 ], [ 13292268 ], [ 10780600 ], [ 12623573 ], [ 14403561 ], [ 12418717 ], [ 14327229 ], [ 15517403 ], [ 14515069 ], [ 15764368 ], [ 16366271 ], [ 0xABABAB ], [ 0xD4D4D4 ], [ 0xFFFFFF ], [ 14256481 ], [ 14656129 ], [ 15848130 ], [ 14005087 ], [ 14337152 ], [ 15918540 ], [ 15118118 ], [ 15531929 ], [ 9764857 ], [ 11258085 ] ];
|
||||
case 2:
|
||||
return [[16579283], [15378351], [8830016], [15257125], [9340985], [8949607], [6198292], [8703620], [9889626], [8972045], [12161285], [13162269], [8620113], [12616503], [8628101], [0xD2FF00], [9764857]];
|
||||
return [ [ 16579283 ], [ 15378351 ], [ 8830016 ], [ 15257125 ], [ 9340985 ], [ 8949607 ], [ 6198292 ], [ 8703620 ], [ 9889626 ], [ 8972045 ], [ 12161285 ], [ 13162269 ], [ 8620113 ], [ 12616503 ], [ 8628101 ], [ 0xD2FF00 ], [ 9764857 ] ];
|
||||
case 3:
|
||||
return [[0xFFFFFF], [0xEEEEEE], [0xDDDDDD]];
|
||||
return [ [ 0xFFFFFF ], [ 0xEEEEEE ], [ 0xDDDDDD ] ];
|
||||
case 4:
|
||||
return [[0xFFFFFF], [16053490], [15464440], [16248792], [15396319], [15007487]];
|
||||
return [ [ 0xFFFFFF ], [ 16053490 ], [ 15464440 ], [ 16248792 ], [ 15396319 ], [ 15007487 ] ];
|
||||
case 5:
|
||||
return [[0xFFFFFF], [0xEEEEEE], [0xDDDDDD]];
|
||||
return [ [ 0xFFFFFF ], [ 0xEEEEEE ], [ 0xDDDDDD ] ];
|
||||
case 6:
|
||||
return [[0xFFFFFF], [0xEEEEEE], [0xDDDDDD], [16767177], [16770205], [16751331]];
|
||||
return [ [ 0xFFFFFF ], [ 0xEEEEEE ], [ 0xDDDDDD ], [ 16767177 ], [ 16770205 ], [ 16751331 ] ];
|
||||
case 7:
|
||||
return [[0xCCCCCC], [0xAEAEAE], [16751331], [10149119], [16763290], [16743786]];
|
||||
return [ [ 0xCCCCCC ], [ 0xAEAEAE ], [ 16751331 ], [ 10149119 ], [ 16763290 ], [ 16743786 ] ];
|
||||
default: {
|
||||
const colors: number[][] = [];
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer';
|
||||
import { GetProductDataForLocalization, SendMessageComposer } from '../../../api';
|
||||
import { GetProductDataForLocalization, SendMessageComposer } from '..';
|
||||
import { ICatalogPage } from './ICatalogPage';
|
||||
import { IProduct } from './IProduct';
|
||||
import { IPurchasableOffer } from './IPurchasableOffer';
|
19
src/api/catalog/GetImageIconUrlForProduct.ts
Normal file
19
src/api/catalog/GetImageIconUrlForProduct.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { GetRoomEngine } from '../nitro';
|
||||
import { ProductTypeEnum } from './ProductTypeEnum';
|
||||
|
||||
export const GetImageIconUrlForProduct = (productType: string, productClassId: number, extraData: string = null) =>
|
||||
{
|
||||
let imageUrl: string = null;
|
||||
|
||||
switch(productType.toLocaleLowerCase())
|
||||
{
|
||||
case ProductTypeEnum.FLOOR:
|
||||
imageUrl = GetRoomEngine().getFurnitureFloorIconUrl(productClassId);
|
||||
break;
|
||||
case ProductTypeEnum.WALL:
|
||||
imageUrl = GetRoomEngine().getFurnitureWallIconUrl(productClassId, extraData);
|
||||
break;
|
||||
}
|
||||
|
||||
return imageUrl;
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { ClubGiftInfoParser, ClubOfferData, HabboGroupEntryData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer';
|
||||
import { CatalogPetPalette } from './CatalogPetPalette';
|
||||
import { GiftWrappingConfiguration } from './GiftWrappingConfiguration';
|
||||
import { SubscriptionInfo } from './SubscriptionInfo';
|
||||
|
||||
export interface ICatalogOptions
|
||||
{
|
||||
@ -9,7 +8,6 @@ export interface ICatalogOptions
|
||||
petPalettes?: CatalogPetPalette[];
|
||||
clubOffers?: ClubOfferData[];
|
||||
clubGifts?: ClubGiftInfoParser;
|
||||
subscriptionInfo?: SubscriptionInfo;
|
||||
giftConfiguration?: GiftWrappingConfiguration;
|
||||
marketplaceConfiguration?: MarketplaceConfigurationMessageParser;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export interface IMarketplaceSearchOptions {
|
||||
export interface IMarketplaceSearchOptions
|
||||
{
|
||||
query: string;
|
||||
type: number;
|
||||
minPrice: number;
|
@ -1,9 +1,8 @@
|
||||
import { GetFurnitureData, GetProductDataForLocalization, LocalizeText } from '../../../api';
|
||||
import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, ProductTypeEnum } from '..';
|
||||
import { ICatalogPage } from './ICatalogPage';
|
||||
import { IProduct } from './IProduct';
|
||||
import { IPurchasableOffer } from './IPurchasableOffer';
|
||||
import { Product } from './Product';
|
||||
import { ProductTypeEnum } from './ProductTypeEnum';
|
||||
|
||||
export class Offer implements IPurchasableOffer
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
import { GetConfiguration } from '../../../api';
|
||||
import { GetConfiguration } from '../nitro';
|
||||
import { IPageLocalization } from './IPageLocalization';
|
||||
|
||||
export class PageLocalization implements IPageLocalization
|
41
src/api/catalog/PlacedObjectPurchaseData.ts
Normal file
41
src/api/catalog/PlacedObjectPurchaseData.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer';
|
||||
import { IPurchasableOffer } from './IPurchasableOffer';
|
||||
|
||||
export class PlacedObjectPurchaseData
|
||||
{
|
||||
constructor(
|
||||
public readonly roomId: number,
|
||||
public readonly objectId: number,
|
||||
public readonly category: number,
|
||||
public readonly wallLocation: string,
|
||||
public readonly x: number,
|
||||
public readonly y: number,
|
||||
public readonly direction: number,
|
||||
public readonly offer: IPurchasableOffer)
|
||||
{}
|
||||
|
||||
public get offerId(): number
|
||||
{
|
||||
return this.offer.offerId;
|
||||
}
|
||||
|
||||
public get productClassId(): number
|
||||
{
|
||||
return this.offer.product.productClassId;
|
||||
}
|
||||
|
||||
public get productData(): IProductData
|
||||
{
|
||||
return this.offer.product.productData;
|
||||
}
|
||||
|
||||
public get furniData(): IFurnitureData
|
||||
{
|
||||
return this.offer.product.furnitureData;
|
||||
}
|
||||
|
||||
public get extraParam(): string
|
||||
{
|
||||
return this.offer.product.extraParam;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { IFurnitureData, IObjectData, IProductData } from '@nitrots/nitro-renderer';
|
||||
import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../../../api';
|
||||
import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../nitro';
|
||||
import { GetPixelEffectIcon, GetSubscriptionProductIcon } from './CatalogUtilities';
|
||||
import { IProduct } from './IProduct';
|
||||
import { IPurchasableOffer } from './IPurchasableOffer';
|
11
src/api/catalog/ProductTypeEnum.ts
Normal file
11
src/api/catalog/ProductTypeEnum.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export class ProductTypeEnum
|
||||
{
|
||||
public static WALL: string = 'i';
|
||||
public static FLOOR: string = 's';
|
||||
public static EFFECT: string = 'e';
|
||||
public static HABBO_CLUB: string = 'h';
|
||||
public static BADGE: string = 'b';
|
||||
public static GAME_TOKEN: string = 'GAME_TOKEN';
|
||||
public static PET: string = 'p';
|
||||
public static ROBOT: string = 'r';
|
||||
}
|
@ -6,6 +6,6 @@ export class SearchResult
|
||||
constructor(
|
||||
public readonly searchValue: string,
|
||||
public readonly offers: IPurchasableOffer[],
|
||||
public readonly filteredNodes: ICatalogNode[]
|
||||
) {}
|
||||
public readonly filteredNodes: ICatalogNode[])
|
||||
{}
|
||||
}
|
29
src/api/catalog/index.ts
Normal file
29
src/api/catalog/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export * from './BuilderFurniPlaceableStatus';
|
||||
export * from './CatalogNode';
|
||||
export * from './CatalogPage';
|
||||
export * from './CatalogPageName';
|
||||
export * from './CatalogPetPalette';
|
||||
export * from './CatalogPurchaseState';
|
||||
export * from './CatalogType';
|
||||
export * from './CatalogUtilities';
|
||||
export * from './FurnitureOffer';
|
||||
export * from './GetImageIconUrlForProduct';
|
||||
export * from './GiftWrappingConfiguration';
|
||||
export * from './ICatalogNode';
|
||||
export * from './ICatalogOptions';
|
||||
export * from './ICatalogPage';
|
||||
export * from './IMarketplaceSearchOptions';
|
||||
export * from './IPageLocalization';
|
||||
export * from './IProduct';
|
||||
export * from './IPurchasableOffer';
|
||||
export * from './IPurchaseOptions';
|
||||
export * from './MarketplaceOfferData';
|
||||
export * from './MarketplaceOfferState';
|
||||
export * from './MarketplaceSearchType';
|
||||
export * from './Offer';
|
||||
export * from './PageLocalization';
|
||||
export * from './PlacedObjectPurchaseData';
|
||||
export * from './Product';
|
||||
export * from './ProductTypeEnum';
|
||||
export * from './RequestedPage';
|
||||
export * from './SearchResult';
|
@ -2,4 +2,5 @@ export class ChatEntryType
|
||||
{
|
||||
public static TYPE_CHAT = 1;
|
||||
public static TYPE_ROOM_INFO = 2;
|
||||
public static TYPE_IM = 3;
|
||||
}
|
6
src/api/chat-history/ChatHistoryCurrentDate.ts
Normal file
6
src/api/chat-history/ChatHistoryCurrentDate.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const ChatHistoryCurrentDate = () =>
|
||||
{
|
||||
const currentTime = new Date();
|
||||
|
||||
return `${ currentTime.getHours().toString().padStart(2, '0') }:${ currentTime.getMinutes().toString().padStart(2, '0') }`;
|
||||
}
|
5
src/api/chat-history/IRoomHistoryEntry.ts
Normal file
5
src/api/chat-history/IRoomHistoryEntry.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface IRoomHistoryEntry
|
||||
{
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
6
src/api/chat-history/MessengerHistoryCurrentDate.ts
Normal file
6
src/api/chat-history/MessengerHistoryCurrentDate.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const MessengerHistoryCurrentDate = (secondsSinceNow: number = 0) =>
|
||||
{
|
||||
const currentTime = secondsSinceNow ? new Date(Date.now() - secondsSinceNow * 1000) : new Date();
|
||||
|
||||
return `${ currentTime.getHours().toString().padStart(2, '0') }:${ currentTime.getMinutes().toString().padStart(2, '0') }`;
|
||||
}
|
5
src/api/chat-history/index.ts
Normal file
5
src/api/chat-history/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from './ChatEntryType';
|
||||
export * from './ChatHistoryCurrentDate';
|
||||
export * from './IChatEntry';
|
||||
export * from './IRoomHistoryEntry';
|
||||
export * from './MessengerHistoryCurrentDate';
|
@ -1 +0,0 @@
|
||||
export * from './ProductImageUtility';
|
3
src/api/events/DispatchEvent.ts
Normal file
3
src/api/events/DispatchEvent.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { IEventDispatcher, NitroEvent } from '@nitrots/nitro-renderer';
|
||||
|
||||
export const DispatchEvent = (eventDispatcher: IEventDispatcher, event: NitroEvent) => eventDispatcher.dispatchEvent(event);
|
5
src/api/events/DispatchMainEvent.ts
Normal file
5
src/api/events/DispatchMainEvent.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { NitroEvent } from '@nitrots/nitro-renderer';
|
||||
import { GetNitroInstance } from '../nitro';
|
||||
import { DispatchEvent } from './DispatchEvent';
|
||||
|
||||
export const DispatchMainEvent = (event: NitroEvent) => DispatchEvent(GetNitroInstance().events, event);
|
5
src/api/events/DispatchUiEvent.ts
Normal file
5
src/api/events/DispatchUiEvent.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { NitroEvent } from '@nitrots/nitro-renderer';
|
||||
import { DispatchEvent } from './DispatchEvent';
|
||||
import { UI_EVENT_DISPATCHER } from './UI_EVENT_DISPATCHER';
|
||||
|
||||
export const DispatchUiEvent = (event: NitroEvent) => DispatchEvent(UI_EVENT_DISPATCHER, event);
|
4
src/api/events/index.ts
Normal file
4
src/api/events/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './DispatchEvent';
|
||||
export * from './DispatchMainEvent';
|
||||
export * from './DispatchUiEvent';
|
||||
export * from './UI_EVENT_DISPATCHER';
|
13
src/api/friends/GetGroupChatData.ts
Normal file
13
src/api/friends/GetGroupChatData.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { IGroupChatData } from './IGroupChatData';
|
||||
|
||||
export const GetGroupChatData = (extraData: string) =>
|
||||
{
|
||||
if(!extraData || !extraData.length) return null;
|
||||
|
||||
const splitData = extraData.split('/');
|
||||
const username = splitData[0];
|
||||
const figure = splitData[1];
|
||||
const userId = parseInt(splitData[2]);
|
||||
|
||||
return ({ username: username, figure: figure, userId: userId } as IGroupChatData);
|
||||
}
|
6
src/api/friends/IGroupChatData.ts
Normal file
6
src/api/friends/IGroupChatData.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface IGroupChatData
|
||||
{
|
||||
username: string;
|
||||
figure: string;
|
||||
userId: number;
|
||||
}
|
@ -2,7 +2,7 @@ import { FriendParser } from '@nitrots/nitro-renderer';
|
||||
|
||||
export class MessengerFriend
|
||||
{
|
||||
public static RELATIONSHIP_NONE: number = 0;
|
||||
public static RELATIONSHIP_NONE: number = 0;
|
||||
public static RELATIONSHIP_HEART: number = 1;
|
||||
public static RELATIONSHIP_SMILE: number = 2;
|
||||
public static RELATIONSHIP_BOBBA: number = 3;
|
@ -1,4 +1,4 @@
|
||||
export class GroupType
|
||||
export class MessengerGroupType
|
||||
{
|
||||
public static readonly GROUP_CHAT = 0;
|
||||
public static readonly PRIVATE_CHAT = 1;
|
6
src/api/friends/MessengerIconState.ts
Normal file
6
src/api/friends/MessengerIconState.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export class MessengerIconState
|
||||
{
|
||||
public static HIDDEN: number = 0;
|
||||
public static SHOW: number = 1;
|
||||
public static UNREAD: number = 2;
|
||||
}
|
@ -11,10 +11,10 @@ export class MessengerRequest
|
||||
{
|
||||
if(!data) return false;
|
||||
|
||||
this._id = data.requestId;
|
||||
this._name = data.requesterName;
|
||||
this._figureString = data.figureString;
|
||||
this._requesterUserId = data.requesterUserId;
|
||||
this._id = data.requestId;
|
||||
this._name = data.requesterName;
|
||||
this._figureString = data.figureString;
|
||||
this._requesterUserId = data.requesterUserId;
|
||||
|
||||
return true;
|
||||
}
|
@ -6,5 +6,6 @@ export class MessengerSettings
|
||||
public userFriendLimit: number = 0,
|
||||
public normalFriendLimit: number = 0,
|
||||
public extendedFriendLimit: number = 0,
|
||||
public categories: FriendCategoryData[] = []) {}
|
||||
public categories: FriendCategoryData[] = [])
|
||||
{}
|
||||
}
|
@ -1,50 +1,46 @@
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { GroupType } from './GroupType';
|
||||
import { GetGroupChatData } from './GetGroupChatData';
|
||||
import { MessengerFriend } from './MessengerFriend';
|
||||
import { MessengerGroupType } from './MessengerGroupType';
|
||||
import { MessengerThreadChat } from './MessengerThreadChat';
|
||||
import { MessengerThreadChatGroup } from './MessengerThreadChatGroup';
|
||||
import { getGroupChatData } from './Utils';
|
||||
|
||||
export class MessengerThread
|
||||
{
|
||||
public static MESSAGE_RECEIVED: string = 'MT_MESSAGE_RECEIVED';
|
||||
public static THREAD_ID: number = 0;
|
||||
|
||||
private _threadId: number;
|
||||
private _participant: MessengerFriend;
|
||||
private _groups: MessengerThreadChatGroup[];
|
||||
private _lastUpdated: Date;
|
||||
private _unreadCount: number;
|
||||
|
||||
constructor(participant: MessengerFriend, isNew: boolean = true)
|
||||
constructor(participant: MessengerFriend)
|
||||
{
|
||||
this._threadId = ++MessengerThread.THREAD_ID;
|
||||
this._participant = participant;
|
||||
this._groups = [];
|
||||
this._lastUpdated = new Date();
|
||||
this._unreadCount = 0;
|
||||
|
||||
if(isNew)
|
||||
{
|
||||
this.addMessage(null, LocalizeText('messenger.moderationinfo'), 0, null, MessengerThreadChat.SECURITY_NOTIFICATION);
|
||||
|
||||
this._unreadCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public addMessage(senderId: number, message: string, secondsSinceSent: number = 0, extraData: string = null, type: number = 0): MessengerThreadChat
|
||||
{
|
||||
const isGroupChat = (senderId < 0 && extraData);
|
||||
const userId = isGroupChat ? getGroupChatData(extraData).userId : senderId;
|
||||
const userId = isGroupChat ? GetGroupChatData(extraData).userId : senderId;
|
||||
|
||||
const group = this.getLastGroup(userId);
|
||||
|
||||
if(!group) return;
|
||||
|
||||
if(isGroupChat) group.type = GroupType.GROUP_CHAT;
|
||||
if(isGroupChat) group.type = MessengerGroupType.GROUP_CHAT;
|
||||
|
||||
const chat = new MessengerThreadChat(senderId, message, secondsSinceSent, extraData, type);
|
||||
|
||||
group.addChat(chat);
|
||||
|
||||
this._lastUpdated = new Date();
|
||||
|
||||
this._unreadCount++;
|
||||
|
||||
return chat;
|
||||
@ -68,6 +64,11 @@ export class MessengerThread
|
||||
this._unreadCount = 0;
|
||||
}
|
||||
|
||||
public get threadId(): number
|
||||
{
|
||||
return this._threadId;
|
||||
}
|
||||
|
||||
public get participant(): MessengerFriend
|
||||
{
|
||||
return this._participant;
|
||||
@ -90,6 +91,6 @@ export class MessengerThread
|
||||
|
||||
public get unread(): boolean
|
||||
{
|
||||
return this._unreadCount > 0;
|
||||
return (this._unreadCount > 0);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { GroupType } from './GroupType';
|
||||
import { MessengerGroupType } from './MessengerGroupType';
|
||||
import { MessengerThreadChat } from './MessengerThreadChat';
|
||||
|
||||
export class MessengerThreadChatGroup
|
||||
@ -7,7 +7,7 @@ export class MessengerThreadChatGroup
|
||||
private _chats: MessengerThreadChat[];
|
||||
private _type: number;
|
||||
|
||||
constructor(userId: number, type = GroupType.PRIVATE_CHAT)
|
||||
constructor(userId: number, type = MessengerGroupType.PRIVATE_CHAT)
|
||||
{
|
||||
this._userId = userId;
|
||||
this._chats = [];
|
@ -2,6 +2,6 @@ import { CreateLinkEvent } from '..';
|
||||
|
||||
export function OpenMessengerChat(friendId: number = 0): void
|
||||
{
|
||||
if(friendId === 0) CreateLinkEvent('friends/messenger/open');
|
||||
else CreateLinkEvent(`friends/messenger/${friendId}`);
|
||||
if(friendId === 0) CreateLinkEvent('friends-messenger/toggle');
|
||||
else CreateLinkEvent(`friends-messenger/${ friendId }`);
|
||||
}
|
||||
|
@ -1 +1,11 @@
|
||||
export * from './GetGroupChatData';
|
||||
export * from './IGroupChatData';
|
||||
export * from './MessengerFriend';
|
||||
export * from './MessengerGroupType';
|
||||
export * from './MessengerIconState';
|
||||
export * from './MessengerRequest';
|
||||
export * from './MessengerSettings';
|
||||
export * from './MessengerThread';
|
||||
export * from './MessengerThreadChat';
|
||||
export * from './MessengerThreadChatGroup';
|
||||
export * from './OpenMessengerChat';
|
||||
|
@ -2,5 +2,5 @@ import { CreateLinkEvent } from '..';
|
||||
|
||||
export function GetGroupManager(groupId: number): void
|
||||
{
|
||||
CreateLinkEvent(`groups/manage/${groupId}`);
|
||||
CreateLinkEvent(`groups/manage/${ groupId }`);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
export class GroupBadgePart
|
||||
{
|
||||
public static BASE: string = 'b';
|
||||
@ -18,7 +19,7 @@ export class GroupBadgePart
|
||||
|
||||
public get code(): string
|
||||
{
|
||||
if(this.key === 0) return null;
|
||||
if((this.key === 0) && (this.type !== GroupBadgePart.BASE)) return null;
|
||||
|
||||
return GroupBadgePart.getCode(this.type, this.key, this.color, this.position);
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
export * from './GetGroupInformation';
|
||||
export * from './GetGroupManager';
|
||||
export * from './GetGroupMembers';
|
||||
export * from './GroupBadgePart';
|
||||
export * from './GroupMembershipType';
|
||||
export * from './GroupType';
|
||||
export * from './IGroupCustomize';
|
||||
export * from './IGroupData';
|
||||
export * from './ToggleFavoriteGroup';
|
||||
export * from './TryJoinGroup';
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user