misc.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { Platform } from 'react-native'
  2. import { fetcher } from 'app/lib/fetcher';
  3. import md5Hex from 'blueimp-md5';
  4. import Clipboard from '@react-native-clipboard/clipboard';
  5. import { isWeb } from './layout';
  6. import { appSetting } from './settings';
  7. import type { TFunction } from 'i18next';
  8. export async function subscribeOneSignal(currentUser: { id: number | string; hash?: string }, askPermission = false): Promise<void> {
  9. if (isWeb) return;
  10. const { LogLevel, OneSignal } = await import('react-native-onesignal');
  11. OneSignal.Debug.setLogLevel(LogLevel.None);
  12. OneSignal.initialize(appSetting('config', 'api_keys', 'onesignal'));
  13. if (Platform.OS === "android") {
  14. // TODO: better solution would be to ask user if they want to receive notifications
  15. // and only then request permissions, instead of delay.
  16. await new Promise(resolve => setTimeout(resolve, 15000));
  17. }
  18. let permissionStatus = await OneSignal.Notifications.getPermissionAsync();
  19. //console.log("OneSignal permission status before request:", permissionStatus);
  20. if (!permissionStatus && askPermission) {
  21. await OneSignal.Notifications.requestPermission(true);
  22. permissionStatus = await OneSignal.Notifications.getPermissionAsync();
  23. //console.log("OneSignal permission status after request:", permissionStatus);
  24. }
  25. if (permissionStatus) {
  26. /*console.log("OneSignal data:", {
  27. id: await OneSignal.User.getOnesignalId(),
  28. token: await OneSignal.User.pushSubscription.getTokenAsync(),
  29. optedIn: await OneSignal.User.pushSubscription.getOptedInAsync(),
  30. permission: permissionStatus
  31. });*/
  32. await OneSignal.login(String(currentUser.id));
  33. await OneSignal.User.addTag("user_hash", String(currentUser.hash));
  34. }
  35. // Method for listening for notification clicks
  36. /*OneSignal.Notifications.addEventListener('click', (event) => {
  37. console.log('OneSignal: notification clicked:', event);
  38. });*/
  39. }
  40. export async function getClipboard(): Promise<string> {
  41. if (isWeb) {
  42. return await navigator.clipboard.readText();
  43. }
  44. else {
  45. return await Clipboard.getString();
  46. }
  47. }
  48. export async function setClipboard(str: string): Promise<void> {
  49. if (!isWeb) {
  50. Clipboard.setString(str);
  51. }
  52. else {
  53. await navigator.clipboard.writeText(str);
  54. }
  55. }
  56. export function clearNotif() {
  57. const clearNotifications = async () => {
  58. fetcher('/api.php?r=bx_notifications/mark_as_read/')
  59. }
  60. clearNotifications();
  61. }
  62. /**
  63. * Sync MD5 hex of a string (UTF-8). Used as a stable key for resumable
  64. * uploads — not for anything security-related.
  65. */
  66. export function md5(str: unknown): string {
  67. return md5Hex(String(str ?? ''));
  68. }
  69. /**
  70. * Stable palette color name (`theme.profile_colors`) for an id or title — same input,
  71. * same color on every platform. Used as `bg-${color}-500` for avatar placeholders.
  72. * Keep the palette in sync with the `@source inline("bg-{…}-500")` list in
  73. * design/styles/utilities.css, or web won't generate the classes.
  74. */
  75. export function getRandomColor(str?: string | number | null): string {
  76. const s = str ? String(str) : 'a';
  77. let hash = 0;
  78. for (let i = 0; i < s.length; i++) {
  79. const char = s.charCodeAt(i);
  80. hash = ((hash << 5) - hash) + char;
  81. hash |= 0; // Convert to 32bit integer
  82. }
  83. const palette: string[] = appSetting('theme', 'profile_colors') || [];
  84. // Fallback must be in the utilities.css safelist too (callers append `-500`).
  85. if (!palette.length) return 'orange';
  86. return palette[Math.abs(hash % palette.length)]!;
  87. }
  88. export function visibilityById(visibility: string | number, t: TFunction): { icon: string; text: string } | null {
  89. const visibilityOptions: Record<string | number, { icon: string; text: string }> = {
  90. 2: { icon: 'Lock', text: t('Me only') },
  91. 3: { icon: 'Globe', text: t('Public') },
  92. 5: { icon: 'UsersRound', text: t('Friends') },
  93. 'c': { icon: 'EyeClosed', text: t('Closed') },
  94. 's': { icon: 'Shield', text: t('Secret') },
  95. 6: { icon: 'UserCheck', text: t('Specific Friends...') },
  96. 7: { icon: 'Workflow', text: t('Relationships') },
  97. 8: { icon: 'Workflow', text: t('Specific Relationships...') },
  98. 9: { icon: 'Award', text: t('Specific Memberships...') },
  99. };
  100. return visibilityOptions[visibility] || null;
  101. }
  102. export function genRnd(length: number): string {
  103. let result = '';
  104. const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  105. const charactersLength = characters.length;
  106. let counter = 0;
  107. while (counter < length) {
  108. result += characters.charAt(Math.floor(Math.random() * charactersLength));
  109. counter += 1;
  110. }
  111. return result;
  112. }
  113. export function isNumeric(str: any): boolean {
  114. return !isNaN(str) && !isNaN(parseFloat(str));
  115. }
  116. export function getIconByNameFromIconset<T>(iconset: Record<string, T>, name: string): T | undefined {
  117. let icon = iconset[name];
  118. if (!icon) {
  119. Object.keys(iconset).find((item) => {
  120. if (name.includes(item)) {
  121. icon = iconset[item];
  122. return true;
  123. }
  124. });
  125. }
  126. return icon;
  127. }