@oxyhq/core 3.1.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/AuthManager.js +14 -3
- package/dist/cjs/HttpService.js +89 -0
- package/dist/cjs/OxyServices.js +2 -1
- package/dist/cjs/constants/version.js +1 -1
- package/dist/cjs/i18n/locales/en-US.json +44 -44
- package/dist/cjs/i18n/locales/es-ES.json +44 -44
- package/dist/cjs/i18n/locales/locales/en-US.json +44 -44
- package/dist/cjs/i18n/locales/locales/es-ES.json +44 -44
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/mixins/OxyServices.applications.js +33 -3
- package/dist/cjs/mixins/OxyServices.reputation.js +244 -0
- package/dist/cjs/mixins/OxyServices.workspaces.js +146 -0
- package/dist/cjs/mixins/index.js +4 -2
- package/dist/cjs/utils/accountUtils.js +12 -5
- package/dist/cjs/utils/ssoReturn.js +80 -33
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/AuthManager.js +14 -3
- package/dist/esm/HttpService.js +89 -0
- package/dist/esm/OxyServices.js +2 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/i18n/locales/en-US.json +44 -44
- package/dist/esm/i18n/locales/es-ES.json +44 -44
- package/dist/esm/i18n/locales/locales/en-US.json +44 -44
- package/dist/esm/i18n/locales/locales/es-ES.json +44 -44
- package/dist/esm/index.js +4 -0
- package/dist/esm/mixins/OxyServices.applications.js +33 -3
- package/dist/esm/mixins/OxyServices.reputation.js +241 -0
- package/dist/esm/mixins/OxyServices.workspaces.js +143 -0
- package/dist/esm/mixins/index.js +4 -2
- package/dist/esm/utils/accountUtils.js +12 -5
- package/dist/esm/utils/ssoReturn.js +80 -33
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +57 -0
- package/dist/types/OxyServices.d.ts +2 -1
- package/dist/types/constants/version.d.ts +2 -2
- package/dist/types/index.d.ts +4 -2
- package/dist/types/mixins/OxyServices.applications.d.ts +86 -10
- package/dist/types/mixins/OxyServices.features.d.ts +0 -1
- package/dist/types/mixins/OxyServices.reputation.d.ts +436 -0
- package/dist/types/mixins/OxyServices.workspaces.d.ts +205 -0
- package/dist/types/mixins/index.d.ts +3 -2
- package/dist/types/models/interfaces.d.ts +24 -26
- package/dist/types/utils/accountUtils.d.ts +17 -4
- package/dist/types/utils/ssoReturn.d.ts +30 -9
- package/package.json +2 -1
- package/src/AuthManager.ts +14 -3
- package/src/HttpService.ts +91 -0
- package/src/OxyServices.ts +2 -1
- package/src/__tests__/authManager.cookiePath.test.ts +49 -0
- package/src/__tests__/httpServiceCache.test.ts +198 -0
- package/src/constants/version.ts +1 -1
- package/src/i18n/locales/en-US.json +44 -44
- package/src/i18n/locales/es-ES.json +44 -44
- package/src/index.ts +51 -4
- package/src/mixins/OxyServices.applications.ts +103 -5
- package/src/mixins/OxyServices.auth.ts +2 -1
- package/src/mixins/OxyServices.features.ts +0 -1
- package/src/mixins/OxyServices.reputation.ts +674 -0
- package/src/mixins/OxyServices.workspaces.ts +315 -0
- package/src/mixins/__tests__/reputation.test.ts +408 -0
- package/src/mixins/index.ts +6 -3
- package/src/models/interfaces.ts +25 -32
- package/src/utils/__tests__/accountUtils.test.ts +142 -0
- package/src/utils/__tests__/consumeSsoReturn.test.ts +229 -37
- package/src/utils/accountUtils.ts +20 -5
- package/src/utils/ssoReturn.ts +98 -37
- package/dist/cjs/mixins/OxyServices.developer.js +0 -97
- package/dist/cjs/mixins/OxyServices.karma.js +0 -108
- package/dist/esm/mixins/OxyServices.developer.js +0 -94
- package/dist/esm/mixins/OxyServices.karma.js +0 -105
- package/dist/types/mixins/OxyServices.developer.d.ts +0 -106
- package/dist/types/mixins/OxyServices.karma.d.ts +0 -92
- package/src/mixins/OxyServices.karma.ts +0 -111
package/dist/esm/AuthManager.js
CHANGED
|
@@ -682,7 +682,14 @@ export class AuthManager {
|
|
|
682
682
|
* Get a valid access token, refreshing automatically if expired or expiring soon.
|
|
683
683
|
*/
|
|
684
684
|
async getAccessToken() {
|
|
685
|
-
|
|
685
|
+
// In cookieOnly / cookie-restore flows the active access token lives only in
|
|
686
|
+
// memory (`_lastKnownAccessToken` + httpService) and is intentionally never
|
|
687
|
+
// written to JS storage — the cookieOnly contract forbids persisting tokens
|
|
688
|
+
// in JS-accessible storage. Fall back to the in-memory token when storage has
|
|
689
|
+
// none, otherwise getAccessToken returns null after every cold-boot/reload and
|
|
690
|
+
// standalone API clients (e.g. the Console axios client) send no Authorization
|
|
691
|
+
// header → 401 on every authed endpoint while `isAuthenticated` is still true.
|
|
692
|
+
const token = (await this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN)) ?? this._lastKnownAccessToken;
|
|
686
693
|
if (!token)
|
|
687
694
|
return null;
|
|
688
695
|
try {
|
|
@@ -693,7 +700,9 @@ export class AuthManager {
|
|
|
693
700
|
if (decoded.exp - now < buffer) {
|
|
694
701
|
const refreshed = await this.refreshToken();
|
|
695
702
|
if (refreshed) {
|
|
696
|
-
|
|
703
|
+
// refreshToken() updates both storage and `_lastKnownAccessToken`;
|
|
704
|
+
// prefer storage but fall back to memory for the cookieOnly path.
|
|
705
|
+
return (await this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN)) ?? this._lastKnownAccessToken;
|
|
697
706
|
}
|
|
698
707
|
}
|
|
699
708
|
}
|
|
@@ -887,7 +896,9 @@ export class AuthManager {
|
|
|
887
896
|
const hydrated = {
|
|
888
897
|
id: me.id,
|
|
889
898
|
username: me.username,
|
|
890
|
-
name
|
|
899
|
+
// `User.name` and `RefreshAllAccountUser.name` are the same canonical
|
|
900
|
+
// structured `UserNameResponse` shape, so forward it verbatim.
|
|
901
|
+
name: me.name,
|
|
891
902
|
avatar: me.avatar ?? null,
|
|
892
903
|
email: me.email,
|
|
893
904
|
color: me.color ?? null,
|
package/dist/esm/HttpService.js
CHANGED
|
@@ -537,14 +537,66 @@ export class HttpService {
|
|
|
537
537
|
}
|
|
538
538
|
return headers;
|
|
539
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Derive a stable, non-sensitive identity discriminator for cache scoping.
|
|
542
|
+
*
|
|
543
|
+
* The GET-response cache MUST be partitioned by caller identity: endpoints
|
|
544
|
+
* with optional auth (e.g. `GET /profiles/recommendations`) return different
|
|
545
|
+
* content for an anonymous vs an authenticated caller, and per-user content
|
|
546
|
+
* for different authenticated users. Keying solely on `method:url:data`
|
|
547
|
+
* (the previous behavior) let an anonymous response be served to an
|
|
548
|
+
* authenticated caller — surfacing as "Who to follow" recommending accounts
|
|
549
|
+
* the user already follows after a cold-boot session restore.
|
|
550
|
+
*
|
|
551
|
+
* We use the access token's decoded user id (`userId || id`) rather than the
|
|
552
|
+
* raw JWT so the token never lands in a cache key (no token leakage through
|
|
553
|
+
* any cache-key logging, no key bloat). The acting-as id is folded in because
|
|
554
|
+
* managed-account responses differ per acting identity — and `X-Acting-As`
|
|
555
|
+
* already changes the server response for the same bearer token. Falls back
|
|
556
|
+
* to `'anon'` when there is no token, and to a short FNV-1a hash of the token
|
|
557
|
+
* only if it is present but cannot be decoded (degraded but still partitioned,
|
|
558
|
+
* never colliding anon with authed).
|
|
559
|
+
*/
|
|
560
|
+
computeIdentityTag() {
|
|
561
|
+
const accessToken = this.tokenStore.getAccessToken();
|
|
562
|
+
let principal = 'anon';
|
|
563
|
+
if (accessToken) {
|
|
564
|
+
try {
|
|
565
|
+
const decoded = jwtDecode(accessToken);
|
|
566
|
+
principal = decoded.userId || decoded.id || `t${fnv1a32(accessToken)}`;
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
// Undecodable token — still partition it away from anon and from
|
|
570
|
+
// other tokens via a hash. Never silently fall back to 'anon'.
|
|
571
|
+
principal = `t${fnv1a32(accessToken)}`;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return this._actingAsUserId ? `${principal}~as${this._actingAsUserId}` : principal;
|
|
575
|
+
}
|
|
540
576
|
/**
|
|
541
577
|
* Generate cache key efficiently
|
|
542
578
|
* Uses a content-addressed hash for large payloads so two requests with
|
|
543
579
|
* the same shape but different values never collide on the same key
|
|
544
580
|
* (which would silently serve stale data — e.g. paginated search results,
|
|
545
581
|
* large object updates).
|
|
582
|
+
*
|
|
583
|
+
* The key is identity-scoped: the logical `method:url[:data]` portion is
|
|
584
|
+
* suffixed with ` id=<identityTag>` so two callers with different
|
|
585
|
+
* identities (anon vs authed, or two different users) never share an entry.
|
|
586
|
+
* The identity tag is placed at the END so the key still STARTS with
|
|
587
|
+
* `method:url`, preserving the prefix-based invalidation in
|
|
588
|
+
* `clearCacheByPrefix` (e.g. `GET:/session/user/`) and the base-key matching
|
|
589
|
+
* in `clearCacheEntry`.
|
|
546
590
|
*/
|
|
547
591
|
generateCacheKey(method, url, data) {
|
|
592
|
+
return `${this.generateBaseCacheKey(method, url, data)}${HttpService.CACHE_IDENTITY_DELIM}${this.computeIdentityTag()}`;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Build the identity-agnostic portion of a cache key (`method:url[:data]`).
|
|
596
|
+
* Kept separate so identity scoping is applied in exactly one place
|
|
597
|
+
* (`generateCacheKey`) and cannot drift between the cache and dedupe paths.
|
|
598
|
+
*/
|
|
599
|
+
generateBaseCacheKey(method, url, data) {
|
|
548
600
|
if (!data || (typeof data === 'object' && Object.keys(data).length === 0)) {
|
|
549
601
|
return `${method}:${url}`;
|
|
550
602
|
}
|
|
@@ -773,6 +825,15 @@ export class HttpService {
|
|
|
773
825
|
clearTokens() {
|
|
774
826
|
this.tokenStore.clearTokens();
|
|
775
827
|
this.tokenStore.clearCsrfToken();
|
|
828
|
+
// Drop the response cache on logout. The cache is identity-scoped, so a
|
|
829
|
+
// different user could never read these entries, but a logged-out client
|
|
830
|
+
// must not keep the previous session's personalized data resident in
|
|
831
|
+
// memory (privacy + correct logout semantics). We do NOT clear on
|
|
832
|
+
// `setTokens` because a silent token refresh re-issues a token for the
|
|
833
|
+
// SAME user — the identity tag is unchanged and the warm cache is still
|
|
834
|
+
// valid; clearing there would defeat caching as refreshes fire near
|
|
835
|
+
// every token expiry.
|
|
836
|
+
this.cache.clear();
|
|
776
837
|
this.notifyTokenChange();
|
|
777
838
|
}
|
|
778
839
|
/**
|
|
@@ -826,8 +887,25 @@ export class HttpService {
|
|
|
826
887
|
clearCache() {
|
|
827
888
|
this.cache.clear();
|
|
828
889
|
}
|
|
890
|
+
/**
|
|
891
|
+
* Delete a cache entry by its LOGICAL key (`method:url[:data]`).
|
|
892
|
+
*
|
|
893
|
+
* Because the response cache is identity-scoped — stored keys carry an
|
|
894
|
+
* ` id=<identityTag>` suffix — a caller passing the logical key
|
|
895
|
+
* `GET:/users/<id>` must invalidate that resource for EVERY identity that
|
|
896
|
+
* cached it (e.g. `updateProfile` busting a user representation that may be
|
|
897
|
+
* cached under both the owner's id and a viewer's id). We therefore delete
|
|
898
|
+
* the exact key (for any pre-existing un-suffixed entries) AND every
|
|
899
|
+
* identity-scoped variant `<key> id=*`.
|
|
900
|
+
*/
|
|
829
901
|
clearCacheEntry(key) {
|
|
830
902
|
this.cache.delete(key);
|
|
903
|
+
const identityVariantPrefix = `${key}${HttpService.CACHE_IDENTITY_DELIM}`;
|
|
904
|
+
for (const existing of this.cache.keys()) {
|
|
905
|
+
if (existing.startsWith(identityVariantPrefix)) {
|
|
906
|
+
this.cache.delete(existing);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
831
909
|
}
|
|
832
910
|
/**
|
|
833
911
|
* Delete every cache entry whose key starts with `prefix`.
|
|
@@ -866,3 +944,14 @@ export class HttpService {
|
|
|
866
944
|
this.tokenStore.clearCsrfToken();
|
|
867
945
|
}
|
|
868
946
|
}
|
|
947
|
+
/**
|
|
948
|
+
* Delimiter that separates the logical `method:url[:data]` portion of a
|
|
949
|
+
* cache key from its identity suffix. Always APPENDED, never used to parse
|
|
950
|
+
* a key apart, so the `method:url` prefix stays intact for
|
|
951
|
+
* `clearCacheByPrefix` sweeps and `clearCacheEntry` base-key matching.
|
|
952
|
+
* The `clearCacheEntry` callsites all pass fixed, dataless logical keys
|
|
953
|
+
* (`GET:/users/<id>`, `GET:/session/user/<sessionId>`,
|
|
954
|
+
* `GET:/fedcm/me/authorized-apps`), so this readable suffix can never be
|
|
955
|
+
* ambiguous with a serialized request body.
|
|
956
|
+
*/
|
|
957
|
+
HttpService.CACHE_IDENTITY_DELIM = ' id=';
|
package/dist/esm/OxyServices.js
CHANGED
|
@@ -18,9 +18,10 @@ import { composeOxyServices } from './mixins/index.js';
|
|
|
18
18
|
* - **Privacy**: Blocked and restricted users
|
|
19
19
|
* - **Language**: Language detection and metadata
|
|
20
20
|
* - **Payment**: Payment processing
|
|
21
|
-
* - **
|
|
21
|
+
* - **Reputation**: Reputation system (Oxy Trust)
|
|
22
22
|
* - **Assets**: File upload and asset management
|
|
23
23
|
* - **Applications**: Application, membership, and credential management
|
|
24
|
+
* - **Workspaces**: Workspace and membership management
|
|
24
25
|
* - **Location**: Location-based features
|
|
25
26
|
* - **Analytics**: Analytics tracking
|
|
26
27
|
* - **Devices**: Device management
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export const packageInfo = {
|
|
6
6
|
name: "@oxyhq/services",
|
|
7
7
|
version: "5.2.1",
|
|
8
|
-
description: "Reusable OxyHQ module to handle authentication, user management,
|
|
8
|
+
description: "Reusable OxyHQ module to handle authentication, user management, reputation system (Oxy Trust) and more 🚀",
|
|
9
9
|
main: "lib/commonjs/node/index.js",
|
|
10
10
|
module: "lib/module/node/index.js",
|
|
11
11
|
types: "lib/typescript/node/index.d.ts"
|
|
@@ -125,9 +125,9 @@
|
|
|
125
125
|
"No selling your data"
|
|
126
126
|
]
|
|
127
127
|
},
|
|
128
|
-
"
|
|
129
|
-
"title": "
|
|
130
|
-
"body": "Oxy
|
|
128
|
+
"reputation": {
|
|
129
|
+
"title": "Reputation = Trust & Growth",
|
|
130
|
+
"body": "Oxy Trust is a reputation system that reacts to what you do. Helpful, respectful, constructive actions earn it. Harmful or low‑effort stuff chips it away. More reputation can unlock benefits; low reputation can limit features. It keeps things fair and rewards real contribution."
|
|
131
131
|
},
|
|
132
132
|
"avatar": {
|
|
133
133
|
"title": "Make It Yours",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
},
|
|
138
138
|
"ready": {
|
|
139
139
|
"title": "You're Ready",
|
|
140
|
-
"body": "Explore. Contribute. Earn
|
|
140
|
+
"body": "Explore. Contribute. Earn reputation. Stay in control."
|
|
141
141
|
},
|
|
142
142
|
"actions": {
|
|
143
143
|
"back": "Back",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"noBio": "This user has no bio yet.",
|
|
161
161
|
"joinedOn": "Joined {{date}}",
|
|
162
162
|
"bornOn": "Born {{date}}",
|
|
163
|
-
"
|
|
163
|
+
"reputation": "Reputation",
|
|
164
164
|
"followers": "Followers",
|
|
165
165
|
"following": "Following",
|
|
166
166
|
"more": "+ {{count}} more"
|
|
@@ -354,69 +354,69 @@
|
|
|
354
354
|
"subtitle": "Add another account to switch between them quickly"
|
|
355
355
|
}
|
|
356
356
|
},
|
|
357
|
-
"
|
|
357
|
+
"reputation": {
|
|
358
358
|
"faq": {
|
|
359
|
-
"title": "
|
|
360
|
-
"subtitle": "Frequently asked questions about
|
|
359
|
+
"title": "Reputation FAQ",
|
|
360
|
+
"subtitle": "Frequently asked questions about reputation",
|
|
361
361
|
"search": "Search FAQ...",
|
|
362
362
|
"noResults": "No FAQ items found matching \"{{query}}\"",
|
|
363
363
|
"items": {
|
|
364
364
|
"what": {
|
|
365
|
-
"q": "What is
|
|
366
|
-
"a": "
|
|
365
|
+
"q": "What is reputation?",
|
|
366
|
+
"a": "Reputation is a recognition of your positive actions in the Oxy Ecosystem. It cannot be sent or received directly."
|
|
367
367
|
},
|
|
368
368
|
"earn": {
|
|
369
|
-
"q": "How do I earn
|
|
369
|
+
"q": "How do I earn reputation?",
|
|
370
370
|
"a": "By helping others, reporting bugs, contributing content, and participating in community events."
|
|
371
371
|
},
|
|
372
372
|
"lose": {
|
|
373
|
-
"q": "Can I lose
|
|
374
|
-
"a": "
|
|
373
|
+
"q": "Can I lose reputation?",
|
|
374
|
+
"a": "Reputation may be reduced for negative actions or breaking community rules."
|
|
375
375
|
},
|
|
376
376
|
"use": {
|
|
377
|
-
"q": "What can I do with
|
|
378
|
-
"a": "Unlock rewards, badges, and special features as you earn more
|
|
377
|
+
"q": "What can I do with reputation?",
|
|
378
|
+
"a": "Unlock rewards, badges, and special features as you earn more reputation."
|
|
379
379
|
},
|
|
380
380
|
"transfer": {
|
|
381
|
-
"q": "Can I transfer
|
|
382
|
-
"a": "No,
|
|
381
|
+
"q": "Can I transfer reputation to others?",
|
|
382
|
+
"a": "No, reputation cannot be sent or received. It is only earned by your actions."
|
|
383
383
|
},
|
|
384
384
|
"support": {
|
|
385
385
|
"q": "How do I get support?",
|
|
386
|
-
"a": "Contact Oxy support via the app or website for any
|
|
386
|
+
"a": "Contact Oxy support via the app or website for any reputation-related questions."
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
},
|
|
390
390
|
"rules": {
|
|
391
|
-
"title": "
|
|
392
|
-
"subtitle": "How to earn
|
|
391
|
+
"title": "Reputation Rules",
|
|
392
|
+
"subtitle": "How to earn reputation points",
|
|
393
393
|
"empty": "No rules found."
|
|
394
394
|
},
|
|
395
395
|
"leaderboard": {
|
|
396
|
-
"title": "
|
|
396
|
+
"title": "Reputation Leaderboard",
|
|
397
397
|
"subtitle": "Top contributors in the community",
|
|
398
398
|
"empty": "No leaderboard data."
|
|
399
399
|
},
|
|
400
400
|
"rewards": {
|
|
401
|
-
"title": "
|
|
401
|
+
"title": "Reputation Rewards",
|
|
402
402
|
"subtitle": "Unlock special features and recognition",
|
|
403
|
-
"intro": "Unlock special features and recognition by earning
|
|
403
|
+
"intro": "Unlock special features and recognition by earning reputation!",
|
|
404
404
|
"earlyAccess": {
|
|
405
405
|
"title": "🎉 Early Access",
|
|
406
|
-
"desc": "Get early access to new features with 100+
|
|
406
|
+
"desc": "Get early access to new features with 100+ reputation."
|
|
407
407
|
},
|
|
408
408
|
"badge": {
|
|
409
409
|
"title": "🏅 Community Badge",
|
|
410
|
-
"desc": "Earn a special badge for 500+
|
|
410
|
+
"desc": "Earn a special badge for 500+ reputation."
|
|
411
411
|
},
|
|
412
412
|
"featured": {
|
|
413
413
|
"title": "🌟 Featured Member",
|
|
414
|
-
"desc": "Be featured in the community for 1000+
|
|
414
|
+
"desc": "Be featured in the community for 1000+ reputation."
|
|
415
415
|
},
|
|
416
416
|
"moreComing": "More rewards coming soon!"
|
|
417
417
|
},
|
|
418
418
|
"center": {
|
|
419
|
-
"balance": "
|
|
419
|
+
"balance": "Reputation Balance",
|
|
420
420
|
"actions": {
|
|
421
421
|
"leaderboard": "Leaderboard",
|
|
422
422
|
"rules": "Rules",
|
|
@@ -424,17 +424,17 @@
|
|
|
424
424
|
"rewards": "Rewards",
|
|
425
425
|
"faq": "FAQ"
|
|
426
426
|
},
|
|
427
|
-
"info": "
|
|
428
|
-
"history": "
|
|
429
|
-
"noHistory": "No
|
|
427
|
+
"info": "Reputation can only be earned by positive actions in the Oxy Ecosystem. It cannot be sent or received directly.",
|
|
428
|
+
"history": "Reputation History",
|
|
429
|
+
"noHistory": "No reputation history yet.",
|
|
430
430
|
"noDescription": "No description"
|
|
431
431
|
},
|
|
432
432
|
"about": {
|
|
433
|
-
"title": "About
|
|
434
|
-
"subtitle": "Learn about the
|
|
435
|
-
"intro": "
|
|
433
|
+
"title": "About Reputation",
|
|
434
|
+
"subtitle": "Learn about the reputation system (Oxy Trust)",
|
|
435
|
+
"intro": "Reputation is a recognition of your positive actions in the Oxy Ecosystem. It cannot be sent or received directly, only earned by contributing to the community.",
|
|
436
436
|
"how": {
|
|
437
|
-
"title": "How to Earn
|
|
437
|
+
"title": "How to Earn Reputation",
|
|
438
438
|
"help": "Helping other users",
|
|
439
439
|
"report": "Reporting bugs",
|
|
440
440
|
"contribute": "Contributing content",
|
|
@@ -442,31 +442,31 @@
|
|
|
442
442
|
"other": "Other positive actions"
|
|
443
443
|
},
|
|
444
444
|
"why": {
|
|
445
|
-
"title": "Why
|
|
446
|
-
"text": "
|
|
445
|
+
"title": "Why Reputation?",
|
|
446
|
+
"text": "Reputation unlocks special features and recognition in the Oxy Ecosystem. The more you contribute, the more you earn!"
|
|
447
447
|
}
|
|
448
448
|
},
|
|
449
449
|
"achievements": {
|
|
450
450
|
"unlocked": "Unlocked Achievements",
|
|
451
451
|
"locked": "Locked Achievements",
|
|
452
452
|
"firstStep": "First Step",
|
|
453
|
-
"firstStepDesc": "Earned your first
|
|
453
|
+
"firstStepDesc": "Earned your first reputation point",
|
|
454
454
|
"novice": "Novice",
|
|
455
|
-
"noviceDesc": "Reached 10
|
|
455
|
+
"noviceDesc": "Reached 10 reputation points",
|
|
456
456
|
"contributor": "Contributor",
|
|
457
|
-
"contributorDesc": "Reached 50
|
|
457
|
+
"contributorDesc": "Reached 50 reputation points",
|
|
458
458
|
"risingStar": "Rising Star",
|
|
459
|
-
"risingStarDesc": "Reached 100
|
|
459
|
+
"risingStarDesc": "Reached 100 reputation points",
|
|
460
460
|
"earlyAdopter": "Early Adopter",
|
|
461
461
|
"earlyAdopterDesc": "Been part of the community from the start",
|
|
462
462
|
"communityHero": "Community Hero",
|
|
463
|
-
"communityHeroDesc": "Reached 500
|
|
463
|
+
"communityHeroDesc": "Reached 500 reputation points",
|
|
464
464
|
"legend": "Legend",
|
|
465
|
-
"legendDesc": "Reached 1000
|
|
465
|
+
"legendDesc": "Reached 1000 reputation points",
|
|
466
466
|
"phoenix": "Phoenix",
|
|
467
|
-
"phoenixDesc": "Reached 2500
|
|
467
|
+
"phoenixDesc": "Reached 2500 reputation points",
|
|
468
468
|
"unstoppable": "Unstoppable",
|
|
469
|
-
"unstoppableDesc": "Reached 5000
|
|
469
|
+
"unstoppableDesc": "Reached 5000 reputation points",
|
|
470
470
|
"bugHunter": "Bug Hunter",
|
|
471
471
|
"bugHunterDesc": "Reported helpful bugs",
|
|
472
472
|
"helper": "Helper",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"noBio": "Este usuario aún no tiene biografía.",
|
|
108
108
|
"joinedOn": "Se unió el {{date}}",
|
|
109
109
|
"bornOn": "Nació el {{date}}",
|
|
110
|
-
"
|
|
110
|
+
"reputation": "Reputación",
|
|
111
111
|
"followers": "Seguidores",
|
|
112
112
|
"following": "Siguiendo",
|
|
113
113
|
"more": "+ {{count}} más"
|
|
@@ -613,69 +613,69 @@
|
|
|
613
613
|
"submitSuccess": "¡Comentario enviado correctamente!"
|
|
614
614
|
}
|
|
615
615
|
},
|
|
616
|
-
"
|
|
616
|
+
"reputation": {
|
|
617
617
|
"faq": {
|
|
618
|
-
"title": "Preguntas frecuentes sobre
|
|
619
|
-
"subtitle": "Preguntas frecuentes sobre el sistema de
|
|
618
|
+
"title": "Preguntas frecuentes sobre reputación",
|
|
619
|
+
"subtitle": "Preguntas frecuentes sobre el sistema de reputación",
|
|
620
620
|
"search": "Buscar en las FAQ...",
|
|
621
621
|
"noResults": "No se encontraron preguntas que coincidan con \"{{query}}\"",
|
|
622
622
|
"items": {
|
|
623
623
|
"what": {
|
|
624
|
-
"q": "¿Qué es
|
|
625
|
-
"a": "
|
|
624
|
+
"q": "¿Qué es la reputación?",
|
|
625
|
+
"a": "La reputación es un reconocimiento de tus acciones positivas en el ecosistema de Oxy. No se puede enviar ni recibir directamente."
|
|
626
626
|
},
|
|
627
627
|
"earn": {
|
|
628
|
-
"q": "¿Cómo gano
|
|
628
|
+
"q": "¿Cómo gano reputación?",
|
|
629
629
|
"a": "Ayudando a otros, reportando errores, contribuyendo contenido y participando en eventos de la comunidad."
|
|
630
630
|
},
|
|
631
631
|
"lose": {
|
|
632
|
-
"q": "¿Puedo perder
|
|
633
|
-
"a": "
|
|
632
|
+
"q": "¿Puedo perder reputación?",
|
|
633
|
+
"a": "La reputación puede reducirse por acciones negativas o por incumplir las reglas de la comunidad."
|
|
634
634
|
},
|
|
635
635
|
"use": {
|
|
636
|
-
"q": "¿Qué puedo hacer con
|
|
637
|
-
"a": "Desbloquea recompensas, insignias y funciones especiales a medida que ganas más
|
|
636
|
+
"q": "¿Qué puedo hacer con la reputación?",
|
|
637
|
+
"a": "Desbloquea recompensas, insignias y funciones especiales a medida que ganas más reputación."
|
|
638
638
|
},
|
|
639
639
|
"transfer": {
|
|
640
|
-
"q": "¿Puedo transferir
|
|
641
|
-
"a": "No,
|
|
640
|
+
"q": "¿Puedo transferir reputación a otros?",
|
|
641
|
+
"a": "No, la reputación no se puede enviar ni recibir. Solo se gana con tus acciones."
|
|
642
642
|
},
|
|
643
643
|
"support": {
|
|
644
644
|
"q": "¿Cómo obtengo soporte?",
|
|
645
|
-
"a": "Contacta con el soporte de Oxy a través de la app o el sitio web para cualquier pregunta relacionada con
|
|
645
|
+
"a": "Contacta con el soporte de Oxy a través de la app o el sitio web para cualquier pregunta relacionada con la reputación."
|
|
646
646
|
}
|
|
647
647
|
}
|
|
648
648
|
},
|
|
649
649
|
"rules": {
|
|
650
|
-
"title": "Reglas de
|
|
651
|
-
"subtitle": "Cómo ganar puntos de
|
|
650
|
+
"title": "Reglas de reputación",
|
|
651
|
+
"subtitle": "Cómo ganar puntos de reputación",
|
|
652
652
|
"empty": "No se encontraron reglas."
|
|
653
653
|
},
|
|
654
654
|
"leaderboard": {
|
|
655
|
-
"title": "Clasificación de
|
|
655
|
+
"title": "Clasificación de reputación",
|
|
656
656
|
"subtitle": "Mayores contribuidores de la comunidad",
|
|
657
657
|
"empty": "No hay datos de clasificación."
|
|
658
658
|
},
|
|
659
659
|
"rewards": {
|
|
660
|
-
"title": "Recompensas de
|
|
660
|
+
"title": "Recompensas de reputación",
|
|
661
661
|
"subtitle": "Desbloquea funciones especiales y reconocimiento",
|
|
662
|
-
"intro": "¡Desbloquea funciones especiales y reconocimiento ganando
|
|
662
|
+
"intro": "¡Desbloquea funciones especiales y reconocimiento ganando reputación!",
|
|
663
663
|
"earlyAccess": {
|
|
664
664
|
"title": "🎉 Acceso anticipado",
|
|
665
|
-
"desc": "Obtén acceso anticipado a nuevas funciones con 100+ de
|
|
665
|
+
"desc": "Obtén acceso anticipado a nuevas funciones con 100+ de reputación."
|
|
666
666
|
},
|
|
667
667
|
"badge": {
|
|
668
668
|
"title": "🏅 Insignia de la comunidad",
|
|
669
|
-
"desc": "Gana una insignia especial con 500+ de
|
|
669
|
+
"desc": "Gana una insignia especial con 500+ de reputación."
|
|
670
670
|
},
|
|
671
671
|
"featured": {
|
|
672
672
|
"title": "🌟 Miembro destacado",
|
|
673
|
-
"desc": "Destácate en la comunidad con 1000+ de
|
|
673
|
+
"desc": "Destácate en la comunidad con 1000+ de reputación."
|
|
674
674
|
},
|
|
675
675
|
"moreComing": "¡Más recompensas próximamente!"
|
|
676
676
|
},
|
|
677
677
|
"center": {
|
|
678
|
-
"balance": "Saldo de
|
|
678
|
+
"balance": "Saldo de reputación",
|
|
679
679
|
"actions": {
|
|
680
680
|
"leaderboard": "Clasificación",
|
|
681
681
|
"rules": "Reglas",
|
|
@@ -683,17 +683,17 @@
|
|
|
683
683
|
"rewards": "Recompensas",
|
|
684
684
|
"faq": "FAQ"
|
|
685
685
|
},
|
|
686
|
-
"info": "
|
|
687
|
-
"history": "Historial de
|
|
688
|
-
"noHistory": "Aún no hay historial de
|
|
686
|
+
"info": "La reputación solo se puede ganar con acciones positivas en el ecosistema de Oxy. No se puede enviar ni recibir directamente.",
|
|
687
|
+
"history": "Historial de reputación",
|
|
688
|
+
"noHistory": "Aún no hay historial de reputación.",
|
|
689
689
|
"noDescription": "Sin descripción"
|
|
690
690
|
},
|
|
691
691
|
"about": {
|
|
692
|
-
"title": "Acerca
|
|
693
|
-
"subtitle": "Aprende sobre el sistema de
|
|
694
|
-
"intro": "
|
|
692
|
+
"title": "Acerca de la reputación",
|
|
693
|
+
"subtitle": "Aprende sobre el sistema de reputación (Oxy Trust)",
|
|
694
|
+
"intro": "La reputación es un reconocimiento de tus acciones positivas en el ecosistema de Oxy. No se puede enviar ni recibir directamente, solo se gana contribuyendo a la comunidad.",
|
|
695
695
|
"how": {
|
|
696
|
-
"title": "Cómo ganar
|
|
696
|
+
"title": "Cómo ganar reputación",
|
|
697
697
|
"help": "Ayudando a otros usuarios",
|
|
698
698
|
"report": "Reportando errores",
|
|
699
699
|
"contribute": "Contribuyendo contenido",
|
|
@@ -701,31 +701,31 @@
|
|
|
701
701
|
"other": "Otras acciones positivas"
|
|
702
702
|
},
|
|
703
703
|
"why": {
|
|
704
|
-
"title": "¿Por qué
|
|
705
|
-
"text": "
|
|
704
|
+
"title": "¿Por qué la reputación?",
|
|
705
|
+
"text": "La reputación desbloquea funciones especiales y reconocimiento en el ecosistema de Oxy. ¡Cuanto más contribuyas, más ganarás!"
|
|
706
706
|
}
|
|
707
707
|
},
|
|
708
708
|
"achievements": {
|
|
709
709
|
"unlocked": "Logros desbloqueados",
|
|
710
710
|
"locked": "Logros bloqueados",
|
|
711
711
|
"firstStep": "Primer paso",
|
|
712
|
-
"firstStepDesc": "Ganaste tu primer punto de
|
|
712
|
+
"firstStepDesc": "Ganaste tu primer punto de reputación",
|
|
713
713
|
"novice": "Novato",
|
|
714
|
-
"noviceDesc": "Alcanzaste 10 puntos de
|
|
714
|
+
"noviceDesc": "Alcanzaste 10 puntos de reputación",
|
|
715
715
|
"contributor": "Contribuidor",
|
|
716
|
-
"contributorDesc": "Alcanzaste 50 puntos de
|
|
716
|
+
"contributorDesc": "Alcanzaste 50 puntos de reputación",
|
|
717
717
|
"risingStar": "Estrella en ascenso",
|
|
718
|
-
"risingStarDesc": "Alcanzaste 100 puntos de
|
|
718
|
+
"risingStarDesc": "Alcanzaste 100 puntos de reputación",
|
|
719
719
|
"earlyAdopter": "Pionero",
|
|
720
720
|
"earlyAdopterDesc": "Parte de la comunidad desde el inicio",
|
|
721
721
|
"communityHero": "Héroe de la comunidad",
|
|
722
|
-
"communityHeroDesc": "Alcanzaste 500 puntos de
|
|
722
|
+
"communityHeroDesc": "Alcanzaste 500 puntos de reputación",
|
|
723
723
|
"legend": "Leyenda",
|
|
724
|
-
"legendDesc": "Alcanzaste 1000 puntos de
|
|
724
|
+
"legendDesc": "Alcanzaste 1000 puntos de reputación",
|
|
725
725
|
"phoenix": "Fénix",
|
|
726
|
-
"phoenixDesc": "Alcanzaste 2500 puntos de
|
|
726
|
+
"phoenixDesc": "Alcanzaste 2500 puntos de reputación",
|
|
727
727
|
"unstoppable": "Imparable",
|
|
728
|
-
"unstoppableDesc": "Alcanzaste 5000 puntos de
|
|
728
|
+
"unstoppableDesc": "Alcanzaste 5000 puntos de reputación",
|
|
729
729
|
"bugHunter": "Cazador de errores",
|
|
730
730
|
"bugHunterDesc": "Reportaste errores útiles",
|
|
731
731
|
"helper": "Ayudante",
|
|
@@ -842,9 +842,9 @@
|
|
|
842
842
|
"No vendemos tus datos"
|
|
843
843
|
]
|
|
844
844
|
},
|
|
845
|
-
"
|
|
846
|
-
"title": "
|
|
847
|
-
"body": "Oxy
|
|
845
|
+
"reputation": {
|
|
846
|
+
"title": "Reputación = Confianza y crecimiento",
|
|
847
|
+
"body": "Oxy Trust es un sistema de reputación que reacciona a lo que haces. Las acciones útiles, respetuosas y constructivas la aumentan. Las acciones dañinas o de poco esfuerzo la reducen. Más reputación puede desbloquear beneficios; poca reputación puede limitar funciones. Mantiene la justicia y recompensa la contribución real."
|
|
848
848
|
},
|
|
849
849
|
"avatar": {
|
|
850
850
|
"title": "Hazlo tuyo",
|
|
@@ -854,7 +854,7 @@
|
|
|
854
854
|
},
|
|
855
855
|
"ready": {
|
|
856
856
|
"title": "¡Todo listo!",
|
|
857
|
-
"body": "Explora. Contribuye. Gana
|
|
857
|
+
"body": "Explora. Contribuye. Gana reputación. Mantén el control."
|
|
858
858
|
},
|
|
859
859
|
"actions": {
|
|
860
860
|
"back": "Atrás",
|