diff --git a/assets/l10n/intl_es.json b/assets/l10n/intl_es.json
deleted file mode 100644
index 118d76c..0000000
--- a/assets/l10n/intl_es.json
+++ /dev/null
@@ -1,750 +0,0 @@
-{
- "signInWithGoogle": "Iniciar sesión con Google",
- "or": "O",
- "signInWithYourAccount": "Inicia sesión con tu cuenta",
- "signInWithApple": "Iniciar sesión con Apple",
- "loginRepresentsAgreementTo": "Iniciar sesión representa un acuerdo para",
- "termsofService": "Términos de servicio",
- "privaceyPolicy": "política de privacidad",
- "tips": "Consejos",
- "searchNoDataTips": "Ingrese la sala o ID de usuario que desea buscar.",
- "games": "Juegos",
- "mine": "Mío",
- "official": "Oficial",
- "party": "Fiesta",
- "other": "Otro",
- "yes": "Sí",
- "bag": "Bolsa",
- "sound2": "Sonido",
- "startYourBrandNewJourney": "Comience su nuevo viaje",
- "maliciousHarassment": "Acoso malicioso",
- "event": "Evento",
- "roomEdit": "Editar habitación",
- "cancelRoomPassword": "¿Estás seguro de que quieres eliminar la contraseña de la sala?",
- "roomMemberFee": "Tarifa de miembro de la sala",
- "roomTheme2": "Tema de la habitación",
- "background": "Fondo",
- "blockedList2": "Lista de bloqueados",
- "roomPassword": "Contraseña de la habitación",
- "numberOfMic": "Número de micrófono",
- "pleaseEnterContent": "Por favor ingresa el contenido",
- "profilePhoto": "Foto de perfil",
- "aboutMe": "Acerca de mí",
- "myRoom": "mi cuarto",
- "noHistoricalRecordsAvailable": "No hay registros históricos disponibles.",
- "inviteNewUsersToEarnCoins": "Invita a nuevos usuarios a ganar monedas.",
- "crateMyRoom": "CREA TU PROPIA HABITACIÓN.",
- "casualInteraction": "Interacción casual",
- "haveGamePlayingTips": "Tienes un juego en progreso. Por favor sal del juego actual. ¿Estás seguro de que quieres salir?",
- "historicalTour": "Tour Histórico",
- "gameCenter": "Centro de juegos",
- "returnToVoiceChat": "¿Volver al chat de voz?",
- "exitGameMode": "Salir del modo de juego",
- "enterTheRoom": "Entra a la habitación",
- "invite": "Invitar",
- "recent": "Reciente",
- "popularEvents": "Eventos populares",
- "inviteGoRoomTips": "Siempre aquí para ti, llueva o haga sol. ¡Entra y saluda!",
- "confirmInviteThisUserToTheRoom": "¿Confirmar la invitación de este usuario (ID:{1}) a la sala?",
- "clearCacheSuccessfully": "Borrar caché exitosamente",
- "sent": "Enviado",
- "keep": "Mantener",
- "open": "Abierto",
- "deleteAccountTips2": "*Si cambia de opinión, puede volver a iniciar sesión en su cuenta actual dentro de los siete días y la restauraremos automáticamente. Si no se produce ninguna restauración dentro de los siete días, la cuenta se eliminará permanentemente.",
- "deleteAccountTips": "Tiene todos los derechos administrativos sobre esta cuenta. Si tiene la intención de eliminar la cuenta, tenga en cuenta los siguientes riesgos asociados con esta operación:\n1. Una vez que la cuenta se elimine correctamente, ya no podrá iniciar sesión en su cuenta actual. La eliminación de la cuenta es una acción permanente.\n2. Una vez que la cuenta se elimine correctamente, no podrá recuperar ningún dato de la cuenta. Toda la información (incluidas habitaciones y amigos), moneda virtual, regalos y artículos virtuales se eliminarán permanentemente y no se podrán restaurar.\n3. Período de reflexión: si no restaura la cuenta, no podrá acceder a la página de compra, a la página de retiro ni a ninguna otra página de la aplicación.\n4. Durante el período de reflexión o después de que la cuenta haya sido eliminada, la página de perfil indicará que se ha eliminado. Para proteger su cuenta de que otros busquen o accedan a ella, su información personal se eliminará de los sistemas relacionados con las funciones diarias. Cuando la eliminación de la cuenta implique seguridad nacional, procedimientos civiles o penales, o la protección de derechos e intereses legítimos de terceros, el equipo oficial se reserva el derecho de rechazar la solicitud de eliminación de la cuenta del usuario.\nSi está seguro de que desea eliminar todos los datos personales de su cuenta actual, haga clic en \"Eliminar cuenta\".",
- "accountDeletionNotice": "Aviso de eliminación de cuenta:",
- "thisUserHasBeenBlacklisted": "Este usuario ha sido incluido en la lista negra.",
- "trend": "Tendencia",
- "like": "Como",
- "more": "Más",
- "discard": "Desechar",
- "catchFirstComment": "Captura el primer comentario.",
- "reply": "Responder",
- "posting": "Destino",
- "multiple": "Múltiple",
- "successfullyRemovedFromTheBlacklist": "¡Eliminado con éxito de la lista negra!",
- "successfullyAddedToTheBlacklist": "¡Agregado exitosamente a la lista negra!",
- "youAreCurrentlyCPRelationshipPleaseDissolve": "Actualmente estás en una relación CP.\nPor favor disuélvalo primero.",
- "areYouSureToCancelBlacklist": "¿Estás seguro de cancelar la lista negra?",
- "areYouSureYouWantToBlockThisUser": "¿Estás seguro de que quieres bloquear a este usuario?",
- "removeFromBlacklist": "Eliminar de la lista negra",
- "moveToBlacklist": "Mover a la lista negra",
- "userBlacklist": "Lista negra de usuarios",
- "specialEffectsManagement": "Manejo de efectos especiales",
- "wishingYouHappinessEveryDay": "Deseándote felicidad todos los días.",
- "newMessage": "Nuevo mensaje",
- "createRoomSuccsess": "¡La habitación se creó correctamente!",
- "contactUs": "Contáctenos",
- "systemAnnouncementTips1": "Protéjase contra el fraude:",
- "systemAnnouncementTips": "Verificar información únicamente a través de canales oficiales. Nunca descargue software de terceros, no comparta datos personales ni transfiera dinero en función de solicitudes externas. Las identificaciones oficiales del personal son solo 10000, 10003 y 10086. Ante cualquier sospecha, deténgase e informe a través de",
- "systemAnnouncement": "Anuncio del sistema",
- "doNotClickUnfamiliarTips": "No haga clic en enlaces desconocidos, ya que pueden exponer su información personal. Nunca compartas tu DNI o los datos de tu tarjeta bancaria con nadie.",
- "atTag": "@Etiqueta",
- "sayHi2": "Di hola",
- "roomBottomGreeting": "Hola...",
- "canSendMsgTips": "Ambas partes deben seguirse antes de poder enviar mensajes privados.",
- "msgSendRedEnvelopeTips": "*Se cobrará una tarifa de servicio del 10 % en los sobres rojos y los destinatarios solo recibirán el 90 % del valor del sobre rojo. El nivel de riqueza del remitente debe ser superior al nivel 10.",
- "reapply": "Aplicar de nuevo",
- "cancelRequest": "Cancelar solicitud",
- "pending": "Pendiente",
- "supporter": "Seguidor",
- "coinsReceived": "Monedas recibidas",
- "numberOfSign": "Número de signo: {1}",
- "hostWeeklyRank": "Clasificación semanal de anfitrión",
- "supporterWeeklyRank": "Clasificación semanal de partidario",
- "memberList": "Lista de miembros",
- "treasureChest": "Cofre del tesoro",
- "applicationRecord": "Registro de solicitud",
- "appUpdateTip": "La aplicación tiene una nueva versión ({1}). ¿Quieres descargarlo ahora?",
- "ownerIncomeCoins": "Ingresos del propietario: {1} monedas",
- "game": "Juego",
- "skip2": "Saltar",
- "skipCountdown": "Saltar {1}",
- "coins4": "monedas",
- "weekStart": "Inicio de semana",
- "forMoreRewardsPleaseCheckTheTaskCenter": "Para obtener más recompensas, consulte el centro de tareas.",
- "kingQuuen": "Rey-Reina",
- "ramadan": "Ramadán",
- "updateNow": "Actualizar ahora",
- "allGames": "Todos los juegos",
- "fishClass": "Clase de pescado",
- "greedyClass": "Clase codiciosa",
- "raceSeries": "Serie de carreras",
- "slotsClass": "Clase de tragamonedas",
- "others": "Otros",
- "hotGames": "Juegos calientes",
- "chatBox": "cuadro de chat",
- "termsOfServicePrivacyPolicyTips": "Al continuar, aceptas los Términos de servicio y la Política de privacidad.",
- "and": " y ",
- "pleaseSelectTheTypeContent": "Seleccione el tipo de contenido ofensivo.",
- "illegalInformation": "Información ilegal",
- "inappropriateContent": "Contenido inapropiado",
- "personalAttack": "Ataque personal",
- "confirm": "Confirmar",
- "spam": "Correo basura",
- "countdownMinutes": "Minutos de cuenta regresiva:",
- "number2": "Número:",
- "fraud": "Fraude",
- "received": "Recibió",
- "currentProgress": "Progreso actual",
- "currentStage": "Etapa actual:{1}",
- "roomReward2": "Recompensa de habitación:{1}",
- "roomReward": "Recompensa de habitación",
- "expirationTime": "tiempo de vencimiento",
- "ownerSendTheRedEnvelope": "El propietario envió monedas de recompensa.",
- "rewardCoins": "Monedas de recompensa:{1} monedas",
- "signInRewardReceived": "Iniciaste sesión exitosamente. Recompensa: {1}",
- "registerRewardReceived": "Recompensa de registro recibida: {1}",
- "lastWeekProgress": "El progreso de la semana pasada.",
- "redEnvelopeTips2": "*Si el sobre rojo no se reclama dentro del plazo, las monedas restantes serán devueltas al usuario que envió el sobre rojo.",
- "goToRecharge": "ir a recargar",
- "deleteAccount2": " Eliminar cuenta ({1}s)",
- "areYouSureYouWantToDeleteYourAccount": " ¿Estás seguro de que quieres eliminar tu cuenta?",
- "insufhcientGoldsGoToRecharge": "Oro insuficiente. ¡Recarga ahora!",
- "coins2": "{1} monedas",
- "remainingNumberTips": "Disponible restante: ({1}/{2})",
- "collectionTimeTips": "Hora de recogida:{1}({2}/{3})",
- "sendARedEnvelope": "enviar un sobre rojo",
- "sendRedPackConfirmTips": "¿Estás seguro de que quieres enviar el paquete rojo?",
- "redEnvelopeSendingRecords": "Registros de envío de sobre rojo:",
- "redEnvelope": "Sobre rojo",
- "roomRedPacketGoldCoins": "Monedas de oro",
- "roomRedPacketQuantity": "Cantidad",
- "roomRedPacketPickupTime": "Hora de recogida",
- "roomRedPacketInMinutes": "En {1} minutos",
- "roomRedPacketClaimNow": "Reclamar ahora",
- "roomRedPacketSendLuckyPack": "Envía un paquete de la suerte",
- "roomRedPacketSending": "Envío...",
- "roomRedPacketSendSuccess": "Paquete de la suerte enviado",
- "roomRedPacketComingSoon": "Muy pronto",
- "roomRedPacketRulesTitle": "Normas",
- "roomRedPacketRulesContent": "1. Puedes gastar monedas para enviar un Lucky Pack. Si otras personas abren tu Lucky Pack, pueden recibir una cantidad aleatoria de monedas.\n\n2. Dos horas después de enviar un Lucky Pack, las monedas no reclamadas se devolverán a su billetera.\n\n3. Después de enviar un Lucky Pack a todo el servidor, se activará un banner de Lucky Pack para todo el servidor, lo que le brindará más atención.\n\n4. Puedes enviar Lucky Packs de habitación a %1$s por día.\n\n5. Los Lucky Packs enviados por usuarios fuera de su región no se pueden reclamar.",
- "roomRedPacketHistoryTitle": "Historial de entrega del paquete de la suerte",
- "roomRedPacketHistoryItemTitle": "Bolsa de la suerte ({1} sobres rojos)",
- "roomRedPacketHistoryStatusSent": "Enviado",
- "roomRedPacketHistoryStatusFinished": "Totalmente reclamado",
- "roomRedPacketHistoryStatusPending": "Pendiente",
- "roomRedPacketHistoryStatusReturned": "Devuelto",
- "roomRedPacketHistoryRefund": "Reembolso: {1}",
- "roomRedPacketClaimRecordsTitle": "Registros de reclamaciones",
- "roomRedPacketChatMessage": "Te envié un sobre rojo, ¡ábrelo ahora mismo!",
- "roomRedPacketReceivedMessage": "Reclama {1} monedas del {2} Lucky Pack",
- "roomRedPacketOpenSubtitle": "El paquete de la suerte ha llegado",
- "roomRedPacketOpenYouGot": "obtienes",
- "roomRedPacketClaimSuccessTips": "Las monedas han sido enviadas a tu billetera.",
- "roomRedPacketOpenSoldOut": "El Lucky Pack ya se ha agotado.",
- "roomRedPacketOpenExpired": "El paquete de la suerte ha caducado.",
- "roomRedPacketRegionNotMatch": "Solo los usuarios de la misma región lingüística pueden reclamar este paquete de la suerte.",
- "roomRedPacketNotStarted": "El paquete de la suerte aún no está listo.",
- "roomRedPacketDailyLimitExceeded": "Se ha alcanzado el límite de envío de hoy.",
- "roomRedPacketInsufficientBalance": "Monedas de oro insuficientes.",
- "roomRedPacketPresenceRequired": "Quédese en la habitación para usar paquetes de la suerte.",
- "roomRedPacketNetworkError": "No se puede procesar el paquete de la suerte en este momento.",
- "roomRedPacketAlreadyClaimed": "Ya has reclamado este paquete de la suerte.",
- "roomRedPacketOpenWaiting": "Se puede abrir en {1} minutos.",
- "roomRedPacketOpenWaitingCountdown": "Disponible para recogida después del {1}",
- "roomRedPacketOkay": "Bueno",
- "roomRedPacketFloatingTitle": "{1} envió un sobre rojo",
- "roomRedPacketFloatingSubtitle": "Envió un sobre rojo",
- "redEnvelopeRecTips2": "Todos los sobres rojos han sido reclamados.",
- "redEnvelopeRecTips3": "¡El tiempo de recogida del sobre rojo ha expirado!",
- "openTheTreasureChest": "Abre el cofre del tesoro",
- "redEnvelopeRecTips1": "Las monedas ganadas se han depositado en su billetera.",
- "redEnvelopeTips1": "Monedas:",
- "roomTools": "Herramientas de la habitación:",
- "entertainment": "Entretenimiento:",
- "reportSucc": "Informe exitoso",
- "pornography": "Pornografía",
- "reportInputTips": "Describa el problema con el mayor detalle posible para que podamos comprenderlo y resolverlo.",
- "cancel": "Cancelar",
- "join": "Unirse",
- "items": "Elementos",
- "vistors": "Visitantes",
- "fans": "aficionados",
- "balanceNotEnough": "Saldo insuficiente de monedas de oro. ¿Quieres ir a recargar?",
- "skip": "Saltar {1}",
- "letGoToWatch": "¡Vamos a mirar!",
- "launchedARocket": "lanzó un cohete",
- "sendUserId": "Enviar ID de usuario:{1}",
- "giveUpIdentity": "renunciar a la identidad",
- "leaveRoomIdentityTips": "¿Estás seguro de que quieres ceder la identidad de la habitación?",
- "joinMemberTips2": "¿Quieres confirmar unirte a la sala como miembro?",
- "sureUnfollowThisRoom": "¿Seguro que dejarás de seguir esta sala?",
- "welcomeMessage": "¡Bienvenido a nuestra aplicación, {nombre}!",
- "settings": "Ajustes",
- "account": "Cuenta",
- "common": "Común",
- "delete": "Borrar",
- "copy": "Copiar",
- "bio": "Biografía",
- "introduction": "Introducción",
- "useCoupontips": "¿Estás seguro de que quieres utilizar el cupón?",
- "searchUserId": "Buscar ID de usuario",
- "sendUser": "Enviar usuario",
- "hobby": "Pasatiempo",
- "sendCoupontips": "¿Estás seguro de que quieres enviar este cupón a este usuario?",
- "youDontHaveAnyCouponsYet": "Aún no tienes ningún cupón.",
- "recall": "Recordar",
- "youHaventFollowed": "No has seguido ninguna habitación",
- "deleteFromMyDevice": "Eliminar de mi dispositivo",
- "deleteOnAllDevices": "Eliminar en todos los dispositivos",
- "messageHasBeenRecalled": "Este mensaje ha sido recordado.",
- "recallThisMessage": "¿Recuerdas este mensaje?",
- "language": "Idioma",
- "feedback": "Comentario",
- "signedin": "Iniciado sesión",
- "receiveSucc": "Reclamado con éxito",
- "about": "Acerca de",
- "aboutUs": "Sobre nosotros",
- "theme": "Tema",
- "wealthLevel": "Nivel rico",
- "userLevel": "Nivel de encanto",
- "goToUpload": "ir a subir",
- "logout": "Cerrar sesión",
- "luck": "Suerte",
- "level": "Nivel",
- "themeGoToUploadTips": "1. La revisión se completará dentro de las 24 horas posteriores a la carga exitosa.\n2. Todas las monedas serán devueltas si la revisión falla.",
- "pleaseUploadAccordingToExample": "Cárguelo de acuerdo con el ejemplo proporcionado.",
- "home": "Hogar",
- "explore": "Explorar",
- "me": "A mí",
- "socialPrivilege": "privilegio social",
- "information": "Información",
- "myPhoto": "mi foto",
- "areYouSureYouWantToSpend3": "*Si la otra parte rechaza la invitación de CP, sus monedas serán devueltas a su billetera.",
- "areYouSureYouWantToSpend": "¿Estás seguro de que quieres gastar?",
- "areYouSureYouWantToSpend2": "enviar una invitación CP a este usuario?",
- "underReview": "En revisión",
- "doYouWantToDeleteIt": "¿Quieres eliminarlo?",
- "chooseFromAblum": "Elige del álbum",
- "spaceBackground": "Fondo espacial",
- "editProfile": "Editar perfil",
- "sendTheCpRequest": "Enviar la solicitud de CP",
- "addCp": "Agregar CP",
- "partWays": "Caminos parciales",
- "reconcile": "Conciliar",
- "separated": "Apartado",
- "areYouSureYouWantToSpend5": "{1} te confesó el sentimiento; si aceptáis, os convertiréis en pareja.",
- "areYouSureYouWantToSpend6": "{1} quiere volver contigo. Si decide conciliar, se restaurarán todos sus datos anteriores.",
- "reconcileInvitationTips": "*Si la otra parte rechaza la invitación de CP, sus monedas serán devueltas a su billetera.",
- "reconcileInvitation": "Conciliar invitación",
- "areYouSureYouWantToSpend4": "enviar una invitación de reconciliación a este usuario?",
- "partWaysTips": "*Si un miembro de una pareja decide separarse, habrá un período de reflexión de 7 días. Durante este tiempo, ambas partes pueden optar por reconciliarse y todos los datos se restaurarán tras la reconciliación. Si no se elige ninguna reconciliación al final del período de 7 días, se borrarán los datos de la pareja.",
- "areYouSureYouWantToPartWaysWithYourCP": "¿Estás seguro de que quieres separarte de tu CP?",
- "timeSpentTogether": "Tiempo que pasamos juntos: {1} días",
- "firstDay": "Primer día:{1}",
- "numberOfMyCPs": "Número de mis CP:({1}/{2})",
- "props": "Accesorios",
- "win": "Ganar",
- "dice": "Dados",
- "rps": "RPS",
- "operationFail": "La operación fracasó.",
- "enterRoomFailedRetry": "No se pudo entrar a la habitación. Por favor inténtalo de nuevo.",
- "voiceConnectionFailedRetry": "La conexión de voz falló. Por favor inténtalo de nuevo.",
- "likedYourComment": "Me gustó tu comentario.",
- "doYouWantToKeepTheDraft": "¿Quieres conservar el borrador?",
- "operationsAreTooFrequent": "Las operaciones son demasiado frecuentes.",
- "luckNumber": "Número de suerte",
- "relationShip": "Relación",
- "couple": "Pareja {1}:",
- "couple2": "Pareja",
- "reject": "Rechazar",
- "accept": "Aceptar",
- "noMatchedCP": "No hay CP coincidente",
- "inviteYouToBecomeBD": "Te invitamos a convertirte en BD.",
- "adminInviteRechargeAgent": "Te invitamos a convertirte en agente de Recarga.",
- "confirmAcceptTheInvitation": "¿Confirmar para aceptar la invitación?",
- "confirmDeclineTheInvitation": "¿Confirmar que rechazas la invitación?",
- "cpInviteRejectedByOther": "La otra parte se negó a convertirse en {1}.",
- "closeFriendInvitationTitle": "Invitación de un amigo cercano",
- "closeFriendInvitationSubtitle": "Haga clic para ver la invitación",
- "closeFriendInvitationExpired": "La invitación para agregar un amigo cercano ha caducado. Esta acción no se puede realizar.",
- "closeFriendRequestDeclined": "Solicitud de amistad rechazada",
- "closeFriendsHaveBecome": "Se han hecho amigos cercanos",
- "otherAcceptedCloseFriendInvite": "La otra parte aceptó su invitación de amigo cercano.",
- "otherDeclinedCloseFriendInvite": "La otra parte rechazó su invitación de amigo cercano.",
- "youAcceptedCloseFriendInvite": "Aceptaste la invitación de amigo cercano de la otra parte.",
- "youDeclinedCloseFriendInvite": "Rechazaste la invitación de amigo cercano de la otra parte.",
- "closeFriendInviteTimedOut": "Se agotó el tiempo de espera para la invitación de amigo cercano.",
- "cpDismissConfirmMessage": "Eliminar esta relación costará {1} monedas. ¿Confirmar la eliminación?",
- "cpRelationCp": "CP",
- "cpRelationBrother": "Hermano",
- "cpRelationSister": "Hermana",
- "host": "Anfitrión",
- "following": "Siguiente",
- "agent": "Agencia",
- "approved": "Aprobado",
- "approvedWithinOneMinute": "Aprobado y mostrado dentro de 1 minuto después del envío",
- "onlineUsers": "Usuarios en línea ({1}/{2}):",
- "applyToJoin": "Aplicar para unirse",
- "supporterList": "Lista de seguidores",
- "hostList": "Lista de anfitriones",
- "upToAdmins": "Hasta {1} administradores",
- "upToMembers": "Hasta {1} miembros",
- "levelPrivileges": "Privilegios de nivel",
- "ra": "REAL ACADEMIA DE BELLAS ARTES",
- "roomAnnouncement": "Anuncio de la habitación",
- "help": "Ayuda",
- "rejected": "Rechazado",
- "boxContributeTips": "La inversión ya se ha realizado hoy. Por favor no vuelvas a invertir.",
- "bd": "BD",
- "coupon": "Cupón",
- "search": "Buscar",
- "get": "Conseguir",
- "inRocket": "en el cohete",
- "roomRocketHelpTips": "1. Enviar regalos en la habitación aumenta la energía del cohete. *1 moneda de oro de regalo = 1 punto de energía de cohete; Los obsequios de la suerte aumentan la energía del cohete en un 4% del valor de la moneda de oro del obsequio.\n2. Una vez que la energía del cohete esté completamente cargada, la sala puede lanzar el cohete. Las recompensas se distribuirán automáticamente después del lanzamiento.\n3. Los diferentes niveles de cohetes ofrecen diferentes recompensas.\n4. Cuando se lanza el cohete, todos los usuarios en la sala pueden reclamar la recompensa del cohete.\n5. La energía del cohete se reinicia a las 00:00 todos los días.",
- "couponRecord": "Registro de uso de cupones",
- "inRoom": "en la habitación",
- "searchCouponHint": "Buscar cupón",
- "giftCounter": "Mostrador de regalos",
- "bDLeaderInviteYouToBecomeBDLeader": "Te invitamos a convertirte en BDLeader",
- "wins": "gana",
- "inviteYouToBecomeHost": "Te invitamos a convertirte en anfitrión.",
- "friends": "Amigos",
- "deleteConversationTips": "¿Estás seguro de que deseas eliminar el historial de chat con este usuario?",
- "propMessagePrompt": "Mensaje de apoyo",
- "inputUserId": "Introduzca el ID de usuario",
- "fromLuckyGifts": "De regalos mágicos/afortunados",
- "receive": "Recibir",
- "checkInSuccessful": "Registro exitoso",
- "sginTips": "Recibirás una recompensa la primera vez que inicies sesión cada día. Si interrumpes tu racha de inicios de sesión, la recompensa se volverá a calcular desde el primer día cuando vuelvas a iniciar sesión.",
- "popular": "Popular",
- "recommend": "Recomendar",
- "follow": "Seguir",
- "history": "Historia",
- "hotRooms": "Habitaciones Calientes",
- "viewMore": "Ver más",
- "noData": "Sin datos",
- "users": "Usuarios",
- "rooms": "Alojamiento",
- "coins": "monedas",
- "unread": "No leído",
- "read": "Leer",
- "image": "[Imagen]",
- "video": "[Video]",
- "sound": "[Sonido]",
- "gift2": "[Regalo]",
- "clickHereToStartChatting": "Di algo.....",
- "receivedAMessage": "[Recibido un mensaje]",
- "confirmSwitchMicModelTips": "¿Confirmas el cambio en modo asiento?",
- "number": "Número",
- "album": "Álbum",
- "camera": "Cámara",
- "system": "Sistema",
- "notifcation": "Notificación",
- "inviteYouToBecomeAgent": "Te invitamos a convertirte en agencia.",
- "myMusic": "mi musica",
- "add": "Agregar",
- "pullToLoadMore": "Tira para cargar más",
- "loadingFailedClickToRetry": "Error al cargar, haga clic para volver a intentarlo",
- "releaseToLoadMore": "Suelta para cargar más",
- "haveMyLimits": "---Tengo mis límites.---",
- "music": "Música",
- "free": "Gratis",
- "charm": "Encanto de regalo",
- "start": "Comenzar",
- "stop": "Detener",
- "chats": "Charlas",
- "refuse": "Rechazar",
- "agree": "Aceptar",
- "thisFeatureIsCurrentlyUnavailable": "Esta característica no está disponible actualmente.",
- "pleaseSelectTheRecipient": "Por favor seleccione el destinatario.",
- "searchMemberIdHint": "Por favor ingrese el ID del miembro",
- "unclaimedRedEnvelopes": "Los sobres rojos no reclamados se reembolsan en 24 horas.",
- "redEnvelopeNotYetClaimed": "Sobre rojo aún no reclamado.",
- "redEnvelopeAmount": "Cantidad del sobre rojo: {1} monedas",
- "sentARedEnvelope": "Envió un sobre rojo.",
- "theRedEnvelopeHasExpired": "El sobre rojo ha caducado.",
- "joinRequest": "Solicitud de unión",
- "scrollToTheBottom": "Desplácese hasta el final",
- "gameRules": "Reglas del juego:",
- "charmGameRulesTips": "Encienda el panel para mostrar las monedas de regalo recibidas de todos los usuarios en el micrófono. 1 moneda = 1 puntuación (Regalo de la suerte: 1 moneda = 0,04 puntuación).",
- "inputYourOldPassword": "Ingrese su antigua contraseña",
- "enterYourOldPassword": "Ingrese su antigua contraseña",
- "setYourPassword": "Establece tu contraseña",
- "enterYourNewPassword": "Ingresa tu nueva contraseña",
- "confirmYourPassword": "Confirma tu contraseña",
- "theTwoPasswordsDoNotMatch": "Las dos contraseñas no coinciden.",
- "resetLoginPasswordtTips2": "La contraseña debe tener entre 8 y 16 caracteres y debe ser una combinación de letras inglesas mayúsculas y minúsculas y números (no solo números).",
- "resetLoginPassword": "Restablecer contraseña de inicio de sesión",
- "resetLoginPasswordtTips1": "Inicie sesión con su ID de usuario o ID personalizado. Es más seguro iniciar sesión con una cuenta yumi.",
- "localMusic": "Música Local",
- "setLoginPassword": "Establecer contraseña de inicio de sesión",
- "confirmSwitchMicThemeTips": "¿Confirmas el cambio de estilo de asiento?",
- "micTheme": "Tema de micrófono",
- "classicMic": "Micrófono clásico {1}",
- "yesterday": "Ayer {1}",
- "monday": "lunes {1}",
- "tuesday": "martes {1}",
- "wednesday": "miércoles {1}",
- "thursday": "jueves {1}",
- "friday": "viernes {1}",
- "saturday": "sábado {1}",
- "sunday": "domingo {1}",
- "acceptedYour": "{1} aceptó tu ",
- "youAccepted": "Aceptaste {1} ",
- "openRedPackDialogTip": "El sobre rojo",
- "micManagement": "Gestión de micrófonos",
- "bdCenter": "Centro BD",
- "rechargeAgency": "Agencia de recarga",
- "adminCenter": "Centro de administración",
- "managerCenter": "Centro de gerentes",
- "goldList": "Lista de oro",
- "followList": "Lista de seguimiento",
- "fansList": "Lista de fans",
- "daily": "A diario",
- "female": "Femenino",
- "male": "Masculino",
- "identity": "Identidad",
- "adjust": "Ajustar",
- "warning": "Advertencia",
- "screenshotTips": "Captura de pantalla (hasta 3)",
- "roomNotice": "Aviso de habitación",
- "roomTheme": "Tema de la habitación",
- "description": "Descripción:",
- "inputDesHint": "Describa el problema con el mayor detalle posible para que podamos comprenderlo y resolverlo.",
- "roomProfilePicture": "Foto de perfil de la habitación",
- "userProfilePicture": "Foto de perfil de usuario",
- "userName": "Nombre de usuario",
- "pleaseSelectTheTypeToProcess": "Por favor seleccione el tipo a procesar:",
- "roomEditing": "Edición de sala",
- "setAccount": "Establecer cuenta",
- "userEditing": "Edición de usuario",
- "enterTheUserId": "Introduzca el ID de usuario",
- "enterTheRoomId": "Ingrese el ID de la habitación",
- "deleteAccount": "Eliminar cuenta",
- "becomeAgent": "Conviértete en agente",
- "enterNickname": "Introduzca el apodo",
- "enterInvitationCode": "ingrese el código de invitación",
- "selectYourCountry": "Selecciona tu país",
- "inviteCode": "Código de invitación",
- "magic": "Magia",
- "luckGiftSpecialEffects": "Efectos de animación de regalo de la suerte.",
- "theVideoSizeCannotExceed": "El tamaño del vídeo no puede exceder los 50M.",
- "weekly": "Semanalmente",
- "customizedGiftRulesContent": "Cómo conseguir mi regalo personalizado:\n1.Determine si el usuario es elegible para recibir un obsequio personalizado según su nivel de riqueza actual.\n(1) Cuando el nivel de riqueza del usuario es ≥35: el usuario puede cargar un vídeo para el regalo personalizado una vez. Usaremos la imagen de perfil actual del usuario como imagen de regalo y el video proporcionado como efecto de regalo en \"Personalizado\". La producción llevará algún tiempo y te avisaremos tan pronto como esté disponible en la tienda.\n(2) Cuando el nivel de riqueza del usuario es ≥45: el usuario puede cargar un vídeo para el regalo personalizado una vez. Usaremos la imagen de perfil actual del usuario como imagen de regalo y el video proporcionado como efecto de regalo en \"Personalizado\". La producción llevará algún tiempo y te avisaremos tan pronto como esté disponible en la tienda.\n2.Puedes participar en actividades específicas en la aplicación y, luego de cumplir con los criterios, contactarnos a través de la sección \"Mensaje\" → \"Contáctanos\". Proporcione capturas de pantalla de la actividad y el vídeo que desea utilizar para el regalo personalizado. Usaremos su foto de perfil actual como imagen de regalo y el video proporcionado como efecto de regalo, luego lo incluiremos en \"Personalizado\". La producción llevará algún tiempo y te avisaremos tan pronto como esté disponible en la tienda.\nPeríodo de validez del regalo personalizado y cómo ampliarlo:\nLos obsequios personalizados exclusivos estarán disponibles en las estanterías durante un período de validez de 30 días. Para permitir que los usuarios influyentes e inspiradores sigan disfrutando de esta nueva experiencia y muestren su estilo personal, los usuarios que poseen obsequios personalizados pueden extender el tiempo de almacenamiento de todos sus obsequios personalizados en 30 días recargando $500 en cualquier mes determinado.",
- "customizedGiftRules": "Reglas de regalo personalizadas",
- "rulesUpload": "Reglas y carga",
- "monthly": "Mensual",
- "message": "Mensaje",
- "clearCache": "Borrar caché",
- "customized": "Personalizado",
- "searchInputHint": "Ingrese el número de cuenta/habitación",
- "kickRoomTips": "Te han echado de la habitación.",
- "roomKickedEntryBlockedTips": "Un administrador lo ha eliminado de la sala y no puede ingresar por ahora.",
- "joinRoomTips": "¡Se unió a la sala!",
- "roomSetting": "Configuración de la habitación",
- "roomDetails": "Detalles de la habitación",
- "systemRoomTips": "Sea cortés y respetuoso. Cualquier contenido pornográfico o vulgar está estrictamente prohibido en yumi. Una vez descubierta, la cuenta será prohibida permanentemente. Cumpla conscientemente con las regulaciones de la plataforma yumi.",
- "copiedToClipboard": "Copiado al portapapeles",
- "recharge": "Recargar",
- "receivedFromALuckyGift": "Recibido de un regalo mágico/afortunado.",
- "followedYou": "te seguí",
- "agentCenter": "Centro de agencia",
- "areYouSureYouWantToClearLocalCache": "¿Está seguro de que desea borrar el caché local?",
- "clearMessage": "Borrar mensajes en pantalla",
- "report": "Informe",
- "coins3": "monedas",
- "giftSpecialEffects": "efectos especiales de regalo",
- "basicFeatures": "Funciones básicas",
- "task": "Tarea",
- "importantReminder": "Recordatorio importante",
- "entryVehicleAnimation": "Animación del vehículo de entrada.",
- "floatingAnimationInGlobal": "Animación flotante en global.",
- "broadcast": "Transmisión",
- "broadcastDisplay": "Pantalla de transmisión",
- "broadcastAllPlaces": "Todos los lugares",
- "broadcastRoomOnly": "en la habitación",
- "broadcastOff": "Cerrar transmisión",
- "entryVehicleAnimation2": "Los usuarios con privilegios VIP4 o superiores pueden utilizar esta función para desactivar las animaciones del vehículo.",
- "dailyTasks": "Tareas Diarias",
- "exclusiveForNewcomers": "Exclusivo para recién llegados",
- "resetsDailyAtMidnight": "Se reinicia diariamente a medianoche",
- "limitedToOneTime": "Limitado a una vez",
- "useMicrophoneForOneMin": "Usa el micrófono durante 1 min.",
- "taskRequirement": "Requisito",
- "taskProgress": "Progreso",
- "rewardClaimedSuccessfully": "Recompensa reclamada exitosamente",
- "rewardClaimFailed": "Reclamación de recompensa fallida",
- "enterRoomConfirmTips": "¿Estás seguro de que quieres entrar a la habitación?",
- "followSucc": "Seguido con éxito",
- "goldListort": "Lista de oro",
- "rechargeList": "Lista de recarga",
- "edit": "Editar",
- "swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*Consejo: deslízate hacia la izquierda en el área de la pantalla flotante para cerrarla rápidamente.",
- "enterThisVoiceChatRoom": "¿Ingresar a esta sala de chat de voz?",
- "go": "Ir",
- "done": "Hecho",
- "improvementTasks": "Tareas de mejora",
- "save": "Ahorrar",
- "kickPrevention": "Prevención de patadas",
- "freeChatSpeak": "Chatear y hablar gratis",
- "nickName": "Apodo",
- "gender": "Género",
- "unFollow": "Dejar de seguir",
- "days": "Días",
- "permanent": "Permanente",
- "country": "País",
- "birthday": "Cumpleaños",
- "man": "Hombre",
- "woman": "Mujer",
- "apple": "Manzana",
- "google": "Google",
- "startVoiceParty": "¡Comienza una fiesta de voz!",
- "enterRoomTips": "{1} Entra en la habitación",
- "roomName": "Nombre de la habitación",
- "roomMember": "Miembro de la sala",
- "pleaseSelectYourCountry": "Por favor seleccione su país.",
- "pleaseSelectYourGender": "Por favor seleccione su género.",
- "pleaseEnterNickname": "Por favor ingrese un apodo.",
- "countryRegion": "País y región",
- "dateOfBirth": "Fecha de nacimiento",
- "mute": "Silenciar",
- "exit": "Salida",
- "mysteriousInvisibility": "Invisibilidad misteriosa",
- "antiBlock": "Antibloqueo",
- "privateChat": "Chat Privado",
- "storeDiscount": "Descuento en tienda {1} de descuento",
- "membershipFreeChatSpeak": "Chat & Speak sin membresía",
- "priorityRoomSorting": "Clasificación de habitaciones prioritarias",
- "userColoredID": "ID de color del usuario",
- "pleaseSelectaItem": "Por favor seleccione un artículo",
- "areYouRureRoRecharge": "¿Estás seguro de que quieres recargar?",
- "mInimize": "Mantener",
- "everyone": "Todos",
- "shop": "Comercio",
- "roomOwner": "Dueño de la habitación",
- "dailyTaskRewardBonus": "Bonificación de recompensa de tarea diaria ({1} XP)",
- "userLevelXPBoost": "Aumento de XP de nivel de usuario ({1} XP)",
- "andAboveUsers": "{1} y usuarios superiores",
- "privileges": "{1} Privilegios",
- "basicPermissions": "Permisos básicos",
- "hostCenter": "Centro anfitrión",
- "allOnMicrophone": "Todo en micrófono",
- "usersOnMicrophone": "Usuarios en el micrófono",
- "allInTheRoom": "todo en la habitación",
- "send": "Enviar",
- "crop": "Cultivo",
- "goToUpgrade": "Ir a actualizar",
- "exclusiveEmojiWillBeReleasedAfterBecoming": "Se lanzarán emoji exclusivos después de convertirse",
- "preventBeingBlocked": "Evitar ser bloqueado",
- "enableRankIncognitoMode": "Habilitar el modo de incógnito de clasificación",
- "avoidBeingKicked": "Evite ser pateado",
- "finish": "Finalizar",
- "takeTheMic": "Toma el micrófono",
- "openTheMic": "Abre el micrófono",
- "muteTheMic": "Silenciar el micrófono",
- "unlockTheMic": "Desbloquea el micrófono",
- "leavelTheMic": "dejar micrófono",
- "lockTheMic": "Bloquear el micrófono",
- "removeTheMic": "quitar el micrófono",
- "inviteToTheMicrophone": "Invitar al micrófono",
- "openUserProfleCard": "Abrir tarjeta de perfil de usuario",
- "obtain": "obtener",
- "win2": "Gana {1}",
- "backTheRoom": "cuarto trasero",
- "toConsume": "consumir",
- "howToUpgrade": "¿Cómo actualizar?",
- "spendCoinsToGainExperiencePoints": "Gasta monedas para ganar puntos de experiencia.",
- "richLevelUpgradeAnswer": "Deja que tu riqueza lo conquiste todo",
- "charmLevelUpgradeAnswer": "Haz que los demás se sometan a tu encanto",
- "higherLevelFancierAvatarFrame": "Insignias/marco de avatar de nivel superior y más elegantes",
- "all": "Todo",
- "gift": "Regalo",
- "chat": "Charlar",
- "owner": "Dueño",
- "store": "Almacenar",
- "admin": "Administración",
- "member": "Miembro",
- "guest": "Invitado",
- "submit": "Entregar",
- "claim": "Afirmar",
- "claimed": "Reclamado",
- "complete": "Completo",
- "shareTo": "Compartir con",
- "copyLink": "Copiar enlace",
- "faceBook": "Facebook",
- "whatsApp": "Whatsapp",
- "snapChat": "Snapchat",
- "taskNamePersonalGameConsume": "Gasto en juegos",
- "taskNameRoomNewMember": "Nuevos miembros de la sala",
- "taskNameRoomOwnerSendRedPacket": "El propietario de la habitación envía un paquete rojo",
- "taskNameRoomOwnerSendGiftUser": "El propietario de la habitación envía un regalo",
- "taskNameRoomMicUser120Min": "Miembros con más de 120 minutos en el micrófono",
- "taskNameRoomMicUser60Min": "Miembros con más de 60 minutos en el micrófono",
- "taskNameRoomMicUser30Min": "Miembros con más de 30 minutos en el micrófono",
- "taskNamePersonalSendGift": "Enviar regalo al usuario",
- "taskNameRoomOnlineUserCount": "Miembros de la sala en línea",
- "taskNameRoomOwnerInviteMic": "Invitar miembro al micrófono",
- "taskNameRoomUserSendGiftGold": "Monedas regaladas por los miembros",
- "taskNameRoomOwnerSendGiftGold": "Monedas regaladas por el dueño de la habitación",
- "taskNamePersonalMagicGiftGold": "Monedas ganadas enviando regalos mágicos",
- "taskNamePersonalLuckyGiftGold": "Monedas ganadas enviando regalos de la suerte",
- "taskNameRoomOwnerMicTime": "El propietario de la sala utiliza el micrófono en la sala",
- "taskNamePersonalActiveInRoom": "Sea activo en las habitaciones de los demás",
- "taskNamePersonalMicInRoom": "Ir al micrófono",
- "dailyCoinBonanzaRulesDetail": "1. Las tareas personales diarias y las tareas diarias del propietario de la sala se pueden completar una vez por usuario al día. Las tareas se reinician a las 00:00:00 hora de Arabia Saudita.\n2. Las tareas personales diarias sólo se pueden completar en las habitaciones de otras personas; Las tareas del propietario de la sala solo se pueden completar en su propia sala.\n3. Las tareas de entrega de regalos solo cuentan los regalos enviados en las salas, no los regalos enviados en los feeds.\n4. Si se crean varias cuentas usando el mismo dispositivo o la misma tarjeta SIM de alguna manera, las recompensas por todas las tareas solo se pueden reclamar una vez.",
- "dailyCoinBonanzaRules": "Reglas diarias de bonanza de monedas",
- "roomOwnerTasks": "Tareas del propietario de la sala",
- "personalTasks": "Tareas personales",
- "noPromptsToday": "No hay tareas hoy.",
- "getPaidToRefer": "Recibe dinero por recomendar",
- "membershipFee": "Cuota de membresía",
- "membershipFeeTips1": "Establezca la tarifa de membresía para su habitación. Los usuarios pueden unirse a su sala pagando la tarifa.",
- "membershipFeeTips2": "Los oros necesarios para que el usuario se convierta en miembro de la sala. El propietario de la sala obtendrá el 50% de los oros.",
- "freePrice": "Tarifa: 0-10000",
- "touristsSendText": "Turista envía texto",
- "touristsTakeToTheMic": "Los turistas toman el micrófono",
- "theMembershipFee": "La cuota de membresía",
- "theModificationsMade": "Las modificaciones realizadas esta vez no se guardarán después de salir.",
- "viewFrame": "Ver marco",
- "enterRoomName": "Introduce el nombre de la habitación",
- "headdress": "Marcos",
- "mountains": "Vehículos",
- "purchaseIsSuccessful": "La compra es exitosa",
- "buy": "Comprar",
- "followed": "Seguido",
- "follow2": "Seguir:{1}",
- "fans2": "Fanáticos:{1}",
- "vistors2": "Visitantes:{1}",
- "personal2": "Personal:",
- "conntinue": "Continuar",
- "confirmBuyTips": "¿Estás seguro de que quieres comprar?",
- "purchase": "Compra",
- "setRoomPassword": "Establecer contraseña de sala",
- "inputRoomPassword": "Introduzca la contraseña de la sala",
- "enter": "Ingresar",
- "deleteCommentTips": "¿Estás seguro de que quieres eliminar este comentario?",
- "deleteSuccessful": "Eliminación exitosa!",
- "itemsLeft": "Artículos restantes",
- "password": "Contraseña",
- "replySucc": "Responder exitosamente",
- "comment": "Comentario",
- "showMore": "Mostrar más",
- "showLess": "Mostrar menos",
- "enterPassword": "Ingrese la contraseña",
- "enterAccount": "Ingresar cuenta",
- "logIn": "Acceso",
- "saySomething": "Di algo...",
- "sayHi": "Di hola..",
- "pleaseChatFfriendly": "Por favor charla amablemente",
- "unLockTheRoom": "Desbloquear la habitación",
- "operationSuccessful": "La operación fue exitosa.",
- "adminByHomeowner": "es establecido como administrador por el propietario de la vivienda.",
- "memberByHomeowner": "es establecido como miembros por el propietario de la vivienda.",
- "touristByHomeowner": "es catalogado como turista por el propietario de la vivienda.",
- "becomeHost": "Solicite convertirse en anfitrión",
- "superFans": "Súper fanáticos:",
- "setUpAnIdentity": "Configurar una identidad",
- "kickedOutOfRoom": "expulsado de la habitación",
- "knapsack": "Mochila",
- "bdLeader": "Líder BD",
- "picture": "Imagen",
- "theImageSizeCannotExceed": "El tamaño de la imagen no puede exceder los 4M.",
- "activity": "Actividad",
- "alreadyAnAdministrator": "Ya soy administrador",
- "alreadyAnMember": "Ya eres miembro",
- "alreadyAnTourist": "ya un turista",
- "touristsCannotSendMessages": "Los turistas no pueden enviar mensajes.",
- "touristsAreNotAllowedToGoOnTheMic": "Los turistas no pueden usar el micrófono",
- "lockTheRoom": "cerrar la habitación",
- "special": "Especial",
- "visitorList": "Lista de visitantes",
- "successfulWear": "Desgaste exitoso",
- "confirmUnUseTips": "¿Confirmas eliminarlo?",
- "custom": "Costumbre",
- "customBackground": "Fondo personalizado",
- "example": "Ejemplo",
- "endPreview": "Vista previa final",
- "selectAgain": "Seleccionar de nuevo",
- "myItems": "Mis artículos",
- "use": "Usar",
- "unUse": "Desequipar",
- "renewal": "Renovación",
- "wallet": "Billetera",
- "profile": "Perfil",
- "giftwall": "Muro de regalos",
- "announcement": "Anuncio",
- "blockedList": "Lista de bloqueados",
- "country2": "País:",
- "sendTo": "Enviar a",
- "credits": "Créditos: {1}",
- "successfullyUnloaded": "descargado exitosamente",
- "expired": "Venció",
- "day": "Día",
- "inUse": "En uso",
- "staticOrGifImage": "Imagen estática / GIF",
- "confirmUseTips": "¿Quieres confirmar su uso?",
- "pleaseUploadUserAvatar": "Por favor sube un avatar.",
- "joinMemberTips": "Si es visitante en la sala, no podrá tomar el micrófono.",
- "giftGivingSuccessful": "Entrega de regalos exitosa.",
- "theAccountPasswordCannotBeEmpty": "La cuenta o contraseña no puede estar vacía.",
- "invitesYouToTheMicrophone": "{1} te invita al micrófono",
- "english": "Inglés",
- "chinese": "Chino",
- "arabic": "árabe",
- "darkMode": "Modo oscuro",
- "lightMode": "Modo de luz",
- "systemDefault": "Valor predeterminado del sistema",
- "pleaseGetOnTheMicFirst": "Por favor, sube al micrófono primero.",
- "roomMusicAddMusic": "Agregar música",
- "roomMusicSelectAll": "Seleccionar todo",
- "roomMusicAddedCount": "Se agregaron {1} canciones",
- "roomMusicEmpty": "sin musica",
- "roomMusicScanFromPhone": "Escanear desde el teléfono",
- "roomMusicTotalSongs": "Total: {1} canciones",
- "roomMusicNotFound": "No se encontró música",
- "roomMusicDeleteConfirm": "¿Eliminar esta música?",
- "roomMusicDeleted": "Música eliminada",
- "roomMusicListMode": "jugar en orden",
- "roomMusicShuffleMode": "Juego aleatorio",
- "roomMusicSingleMode": "Bucle único",
- "duration2": "Duración:{1}",
- "roomRocketNoteTitle": "Nota",
- "roomRocketNoteSectionTitle": "Cada vez que un VIP",
- "roomRocketNoteSectionBody": "1. Cada vez que se compra un nivel VIP, es válido por 30 días y se puede renovar manualmente una vez vencido.",
- "roomRocketRewardEmptyText": "Sin recompensa",
- "roomRocketRecordEmptyText": "Sin registros de recompensa",
- "launchRocket": "Lanzamiento de cohete",
- "roomRocketFuelFullNotice": "¡El combustible para cohetes está lleno! ¡Lanzamiento en {1} s!"
-}
diff --git a/assets/l10n/intl_id.json b/assets/l10n/intl_id.json
deleted file mode 100644
index 83bdbd5..0000000
--- a/assets/l10n/intl_id.json
+++ /dev/null
@@ -1,750 +0,0 @@
-{
- "signInWithGoogle": "Masuk dengan Google",
- "or": "Atau",
- "signInWithYourAccount": "Masuk dengan akun Anda",
- "signInWithApple": "Masuk dengan Apple",
- "loginRepresentsAgreementTo": "Login mewakili persetujuan untuk",
- "termsofService": "Persyaratan layanan",
- "privaceyPolicy": "Kebijakan Privasi",
- "tips": "Kiat",
- "searchNoDataTips": "Masukkan ruangan atau ID pengguna yang ingin Anda cari.",
- "games": "Pertandingan",
- "mine": "Milikku",
- "official": "Resmi",
- "party": "Berpesta",
- "other": "Lainnya",
- "yes": "Ya",
- "bag": "Tas",
- "sound2": "Suara",
- "startYourBrandNewJourney": "Mulailah Perjalanan Baru Anda",
- "maliciousHarassment": "Pelecehan yang berbahaya",
- "event": "Peristiwa",
- "roomEdit": "Pengeditan Ruangan",
- "cancelRoomPassword": "Apakah Anda yakin ingin menghapus kata sandi ruangan?",
- "roomMemberFee": "Biaya Anggota Kamar",
- "roomTheme2": "Tema Kamar",
- "background": "Latar belakang",
- "blockedList2": "Daftar yang Diblokir",
- "roomPassword": "Kata Sandi Kamar",
- "numberOfMic": "Jumlah Mikrofon",
- "pleaseEnterContent": "Silakan masukkan konten",
- "profilePhoto": "Foto Profil",
- "aboutMe": "Tentang saya",
- "myRoom": "Kamarku",
- "noHistoricalRecordsAvailable": "Tidak ada catatan sejarah yang tersedia.",
- "inviteNewUsersToEarnCoins": "Undang pengguna baru untuk mendapatkan koin",
- "crateMyRoom": "BUAT RUANG ANDA SENDIRI.",
- "casualInteraction": "Interaksi Santai",
- "haveGamePlayingTips": "Anda memiliki permainan yang sedang berlangsung. Silakan keluar dari permainan saat ini. Apakah Anda yakin ingin keluar?",
- "historicalTour": "Wisata Sejarah",
- "gameCenter": "Pusat Permainan",
- "returnToVoiceChat": "Kembali ke obrolan suara?",
- "exitGameMode": "Keluar dari Mode Permainan",
- "enterTheRoom": "Masuki ruangan",
- "invite": "Mengundang",
- "recent": "Terkini",
- "popularEvents": "Acara Populer",
- "inviteGoRoomTips": "Selalu di sini untukmu, hujan atau cerah. Mampirlah dan sapa!",
- "confirmInviteThisUserToTheRoom": "Konfirmasikan undangan pengguna ini (ID:{1}) ke ruang?",
- "clearCacheSuccessfully": "Hapus cache berhasil",
- "sent": "Terkirim",
- "keep": "Menyimpan",
- "open": "Membuka",
- "deleteAccountTips2": "*Jika Anda berubah pikiran, Anda dapat masuk kembali ke akun Anda saat ini dalam waktu tujuh hari, dan kami akan memulihkan akun Anda secara otomatis. Jika tidak ada pemulihan yang terjadi dalam waktu tujuh hari, akun akan dihapus secara permanen.",
- "deleteAccountTips": "Anda memiliki hak administratif penuh atas akun ini. Jika Anda bermaksud menghapus akun, harap perhatikan risiko berikut yang terkait dengan operasi ini:\n1. Setelah akun berhasil dihapus, Anda tidak dapat lagi masuk ke akun Anda saat ini. Penghapusan akun merupakan tindakan permanen.\n2. Setelah akun berhasil dihapus, Anda tidak akan dapat memulihkan data akun apa pun. Semua informasi (termasuk kamar dan teman), mata uang virtual, hadiah, dan barang virtual akan dihapus secara permanen dan tidak dapat dipulihkan.\n3. Masa tunggu: Jika Anda tidak memulihkan akun, Anda tidak akan dapat mengakses halaman pembelian, halaman penarikan, atau halaman lainnya di aplikasi.\n4. Selama masa tunggu atau setelah akun dihapus, halaman profil akan menunjukkan bahwa akun tersebut telah dihapus. Untuk melindungi akun Anda agar tidak dicari atau diakses oleh orang lain, informasi pribadi Anda akan dihapus dari sistem yang terkait dengan fungsi sehari-hari. Jika penghapusan akun melibatkan keamanan nasional, proses perdata atau pidana, atau perlindungan hak dan kepentingan sah pihak ketiga, tim resmi berhak menolak permintaan penghapusan akun pengguna.\nJika Anda yakin ingin menghapus semua data pribadi dari akun Anda saat ini, silakan klik \"Hapus Akun\".",
- "accountDeletionNotice": "Pemberitahuan Penghapusan Akun:",
- "thisUserHasBeenBlacklisted": "Pengguna ini telah masuk daftar hitam.",
- "trend": "Kecenderungan",
- "like": "Menyukai",
- "more": "Lagi",
- "discard": "Membuang",
- "catchFirstComment": "Tangkap komentar pertama",
- "reply": "Membalas",
- "posting": "Posting",
- "multiple": "Banyak",
- "successfullyRemovedFromTheBlacklist": "Berhasil dihapus dari daftar hitam!",
- "successfullyAddedToTheBlacklist": "Berhasil ditambahkan ke daftar hitam!",
- "youAreCurrentlyCPRelationshipPleaseDissolve": "Anda sedang menjalin hubungan CP.\nSilakan dilarutkan terlebih dahulu.",
- "areYouSureToCancelBlacklist": "Apakah Anda yakin untuk membatalkan daftar hitam?",
- "areYouSureYouWantToBlockThisUser": "Apakah Anda yakin ingin memblokir pengguna ini?",
- "removeFromBlacklist": "Hapus dari daftar hitam",
- "moveToBlacklist": "Pindah ke daftar hitam",
- "userBlacklist": "Daftar hitam pengguna",
- "specialEffectsManagement": "Manajemen efek khusus",
- "wishingYouHappinessEveryDay": "Semoga Anda bahagia setiap hari.",
- "newMessage": "Pesan baru",
- "createRoomSuccsess": "Ruangan berhasil dibuat!",
- "contactUs": "Hubungi kami",
- "systemAnnouncementTips1": "Penjagaan terhadap penipuan:",
- "systemAnnouncementTips": "Verifikasi informasi hanya melalui saluran resmi. Jangan pernah mengunduh perangkat lunak pihak ketiga, membagikan data pribadi, atau mentransfer uang berdasarkan permintaan eksternal. ID staf resmi hanya 10000, 10003, dan 10086. Jika ada kecurigaan, hentikan dan laporkan melalui",
- "systemAnnouncement": "Pengumuman Sistem",
- "doNotClickUnfamiliarTips": "Jangan klik tautan yang tidak dikenal, karena dapat mengungkap informasi pribadi Anda. Jangan pernah membagikan detail ID atau kartu bank Anda kepada siapa pun.",
- "atTag": "@Menandai",
- "sayHi2": "Katakan Hai",
- "roomBottomGreeting": "Hai...",
- "canSendMsgTips": "Kedua belah pihak harus saling mengikuti sebelum mereka dapat mengirim pesan pribadi.",
- "msgSendRedEnvelopeTips": "*Biaya layanan sebesar 10% akan dikenakan pada amplop merah, dan penerima hanya akan menerima 90% dari nilai amplop merah. Tingkat kekayaan pengirim harus lebih tinggi dari Level 10.",
- "reapply": "Melamar lagi",
- "cancelRequest": "Batalkan Permintaan",
- "pending": "Tertunda",
- "supporter": "Pendukung",
- "coinsReceived": "Koin Diterima",
- "numberOfSign": "Jumlah tanda: {1}",
- "hostWeeklyRank": "Peringkat Mingguan Tuan Rumah",
- "supporterWeeklyRank": "Peringkat Mingguan Suporter",
- "memberList": "Daftar Anggota",
- "treasureChest": "Peti Harta Karun",
- "applicationRecord": "Catatan Aplikasi",
- "appUpdateTip": "Aplikasi ini memiliki versi baru ({1}). Apakah Anda ingin mengunduhnya sekarang?",
- "ownerIncomeCoins": "Pendapatan pemilik: {1} koin",
- "game": "Permainan",
- "skip2": "Melewati",
- "skipCountdown": "Lewati {1} detik",
- "coins4": "Koin",
- "weekStart": "Awal Minggu",
- "forMoreRewardsPleaseCheckTheTaskCenter": "Untuk hadiah lebih lanjut, silakan periksa pusat tugas",
- "kingQuuen": "Raja-Ratu",
- "ramadan": "Ramadan",
- "updateNow": "Perbarui Sekarang",
- "allGames": "Semua Permainan",
- "fishClass": "Kelas Ikan",
- "greedyClass": "Kelas serakah",
- "raceSeries": "Seri Balapan",
- "slotsClass": "Kelas Slot",
- "others": "Yang lain",
- "hotGames": "Permainan Panas",
- "chatBox": "Kotak obrolan",
- "termsOfServicePrivacyPolicyTips": "Dengan melanjutkan, Anda menyetujui Ketentuan Layanan & Kebijakan Privasi",
- "and": " Dan ",
- "pleaseSelectTheTypeContent": "Silakan pilih jenis konten yang menyinggung.",
- "illegalInformation": "Informasi ilegal",
- "inappropriateContent": "Konten yang tidak pantas",
- "personalAttack": "Serangan pribadi",
- "confirm": "Mengonfirmasi",
- "spam": "Spam",
- "countdownMinutes": "Menit Hitung Mundur:",
- "number2": "Nomor:",
- "fraud": "Tipuan",
- "received": "Diterima",
- "currentProgress": "Kemajuan saat ini",
- "currentStage": "Tahap saat ini:{1}",
- "roomReward2": "Hadiah Kamar:{1}",
- "roomReward": "Hadiah Kamar",
- "expirationTime": "Waktu kedaluwarsa",
- "ownerSendTheRedEnvelope": "Pemiliknya mengirimkan koin hadiah.",
- "rewardCoins": "Koin hadiah:{1} koin",
- "signInRewardReceived": "Berhasil masuk. Hadiah: {1}",
- "registerRewardReceived": "Hadiah pendaftaran diterima: {1}",
- "lastWeekProgress": "Kemajuan minggu lalu",
- "redEnvelopeTips2": "*Jika amplop merah tidak diklaim dalam batas waktu, sisa koin akan dikembalikan kepada pengguna yang mengirimkan amplop merah.",
- "goToRecharge": "Pergi untuk mengisi ulang",
- "deleteAccount2": " Hapus Akun ({1}s)",
- "areYouSureYouWantToDeleteYourAccount": " Apakah Anda yakin ingin menghapus akun Anda?",
- "insufhcientGoldsGoToRecharge": "Emas tidak mencukupi. Isi ulang sekarang!",
- "coins2": "{1} Koin",
- "remainingNumberTips": "Sisa tersedia: ({1}/{2})",
- "collectionTimeTips": "Waktu pengambilan:{1}({2}/{3})",
- "sendARedEnvelope": "Kirim amplop merah",
- "sendRedPackConfirmTips": "Apakah Anda yakin ingin mengirim paket merah?",
- "redEnvelopeSendingRecords": "Catatan pengiriman amplop merah:",
- "redEnvelope": "Amplop Merah",
- "roomRedPacketGoldCoins": "Koin Emas",
- "roomRedPacketQuantity": "Kuantitas",
- "roomRedPacketPickupTime": "Waktu Penjemputan",
- "roomRedPacketInMinutes": "Dalam {1} Menit",
- "roomRedPacketClaimNow": "Klaim Sekarang",
- "roomRedPacketSendLuckyPack": "Kirim Paket Keberuntungan",
- "roomRedPacketSending": "Mengirim...",
- "roomRedPacketSendSuccess": "Paket keberuntungan terkirim",
- "roomRedPacketComingSoon": "Segera hadir",
- "roomRedPacketRulesTitle": "Aturan",
- "roomRedPacketRulesContent": "1. Anda dapat menggunakan koin untuk mengirim Paket Keberuntungan. Jika orang lain membuka Paket Keberuntungan Anda, mereka dapat menerima sejumlah koin secara acak.\n\n2. Dua jam setelah Paket Keberuntungan dikirim, koin yang tidak diklaim akan dikembalikan ke dompet Anda.\n\n3. Setelah Anda mengirimkan Paket Keberuntungan di seluruh server, spanduk Paket Keberuntungan di seluruh server akan terpicu, sehingga memberi Anda lebih banyak perhatian.\n\n4. Anda dapat mengirimkan Paket Keberuntungan kamar sebesar %1$s per hari.\n\n5. Paket Keberuntungan yang dikirim oleh pengguna di luar wilayah Anda tidak dapat diklaim.",
- "roomRedPacketHistoryTitle": "Riwayat Pengiriman Paket Keberuntungan",
- "roomRedPacketHistoryItemTitle": "Tas Keberuntungan ({1} amplop merah)",
- "roomRedPacketHistoryStatusSent": "Terkirim",
- "roomRedPacketHistoryStatusFinished": "Diklaim sepenuhnya",
- "roomRedPacketHistoryStatusPending": "Tertunda",
- "roomRedPacketHistoryStatusReturned": "Kembali",
- "roomRedPacketHistoryRefund": "Pengembalian dana: {1}",
- "roomRedPacketClaimRecordsTitle": "Catatan Klaim",
- "roomRedPacketChatMessage": "Saya mengirimi Anda amplop merah, segera buka!",
- "roomRedPacketReceivedMessage": "Klaim {1} koin dari {2} Paket Keberuntungan",
- "roomRedPacketOpenSubtitle": "Paket Keberuntungan Telah Tiba",
- "roomRedPacketOpenYouGot": "Anda Dapatkan",
- "roomRedPacketClaimSuccessTips": "Koin telah dikirim ke dompet Anda.",
- "roomRedPacketOpenSoldOut": "Paket Keberuntungan Sudah Terjual Habis.",
- "roomRedPacketOpenExpired": "Paket Keberuntungan Telah Kedaluwarsa.",
- "roomRedPacketRegionNotMatch": "Hanya pengguna di wilayah bahasa yang sama yang dapat mengklaim paket keberuntungan ini.",
- "roomRedPacketNotStarted": "Paket keberuntungan belum siap.",
- "roomRedPacketDailyLimitExceeded": "Batas pengiriman hari ini telah tercapai.",
- "roomRedPacketInsufficientBalance": "Koin emas tidak mencukupi.",
- "roomRedPacketPresenceRequired": "Harap tetap di kamar untuk menggunakan paket keberuntungan.",
- "roomRedPacketNetworkError": "Tidak dapat memproses paket keberuntungan saat ini.",
- "roomRedPacketAlreadyClaimed": "Anda sudah mengklaim paket keberuntungan ini.",
- "roomRedPacketOpenWaiting": "Dapat dibuka dalam {1} menit.",
- "roomRedPacketOpenWaitingCountdown": "Tersedia Untuk Pengambilan Setelah {1}",
- "roomRedPacketOkay": "Oke",
- "roomRedPacketFloatingTitle": "{1} mengirimkan amplop merah",
- "roomRedPacketFloatingSubtitle": "Mengirim amplop merah",
- "redEnvelopeRecTips2": "Semua amplop merah telah diklaim.",
- "redEnvelopeRecTips3": "Waktu pengambilan amplop merah telah habis!",
- "openTheTreasureChest": "Buka peti harta karun",
- "redEnvelopeRecTips1": "Koin yang diperoleh telah disimpan ke dompet Anda.",
- "redEnvelopeTips1": "Koin:",
- "roomTools": "Peralatan Ruangan:",
- "entertainment": "Hiburan:",
- "reportSucc": "Laporkan berhasil",
- "pornography": "Pornografi",
- "reportInputTips": "Mohon jelaskan masalahnya sedetail mungkin agar kami dapat memahami dan menyelesaikannya.",
- "cancel": "Membatalkan",
- "join": "Bergabung",
- "items": "Barang",
- "vistors": "Pengunjung",
- "fans": "Penggemar",
- "balanceNotEnough": "Saldo koin emas tidak mencukupi. Apakah Anda ingin pergi ke top up?",
- "skip": "Lewati {1}",
- "letGoToWatch": "Ayo pergi menonton!",
- "launchedARocket": "meluncurkan roket",
- "sendUserId": "Kirim ID Pengguna:{1}",
- "giveUpIdentity": "Menyerahkan identitas",
- "leaveRoomIdentityTips": "Apakah Anda yakin ingin melepaskan identitas kamar?",
- "joinMemberTips2": "Apakah Anda ingin mengonfirmasi bergabung dengan ruang sebagai anggota?",
- "sureUnfollowThisRoom": "Yakin untuk berhenti mengikuti ruangan ini?",
- "welcomeMessage": "Selamat datang di aplikasi kami, {name}!",
- "settings": "Pengaturan",
- "account": "Akun",
- "common": "Umum",
- "delete": "Menghapus",
- "copy": "Menyalin",
- "bio": "Biografi",
- "introduction": "Perkenalan",
- "useCoupontips": "Apakah Anda yakin ingin menggunakan kupon tersebut?",
- "searchUserId": "Cari ID pengguna",
- "sendUser": "Kirim Pengguna",
- "hobby": "Hobi",
- "sendCoupontips": "Apakah Anda yakin ingin mengirimkan kupon ini kepada pengguna ini?",
- "youDontHaveAnyCouponsYet": "Anda belum memiliki kupon apa pun.",
- "recall": "Mengingat",
- "youHaventFollowed": "Anda belum mengikuti ruangan mana pun",
- "deleteFromMyDevice": "Hapus dari perangkat saya",
- "deleteOnAllDevices": "Hapus di semua perangkat",
- "messageHasBeenRecalled": "Pesan ini telah ditarik kembali",
- "recallThisMessage": "Ingat pesan ini?",
- "language": "Bahasa",
- "feedback": "Masukan",
- "signedin": "Masuk",
- "receiveSucc": "Berhasil diklaim",
- "about": "Tentang",
- "aboutUs": "Tentang Kami",
- "theme": "Tema",
- "wealthLevel": "Tingkat Kaya",
- "userLevel": "Tingkat Pesona",
- "goToUpload": "Pergi untuk mengunggah",
- "logout": "Keluar",
- "luck": "Keberuntungan",
- "level": "Tingkat",
- "themeGoToUploadTips": "1. Review akan selesai dalam waktu 24 jam setelah upload berhasil.\n2. Semua koin akan dikembalikan jika peninjauan gagal.",
- "pleaseUploadAccordingToExample": "Silakan upload sesuai contoh yang diberikan.",
- "home": "Rumah",
- "explore": "Mengeksplorasi",
- "me": "Aku",
- "socialPrivilege": "Hak istimewa sosial",
- "information": "Informasi",
- "myPhoto": "Foto saya",
- "areYouSureYouWantToSpend3": "*Jika pihak lain menolak undangan CP, koin Anda akan dikembalikan ke dompet Anda.",
- "areYouSureYouWantToSpend": "Apakah Anda yakin ingin membelanjakannya",
- "areYouSureYouWantToSpend2": "untuk mengirim undangan CP ke pengguna ini?",
- "underReview": "Sedang ditinjau",
- "doYouWantToDeleteIt": "Apakah Anda ingin menghapusnya?",
- "chooseFromAblum": "Pilih dari Album",
- "spaceBackground": "Latar Belakang Luar Angkasa",
- "editProfile": "Sunting Profil",
- "sendTheCpRequest": "Kirim permintaan CP",
- "addCp": "Tambahkan CP",
- "partWays": "Bagian Cara",
- "reconcile": "Mendamaikan",
- "separated": "Terpisah",
- "areYouSureYouWantToSpend5": "{1} menyatakan perasaannya padamu; jika kamu menerimanya, kamu akan menjadi pasangan.",
- "areYouSureYouWantToSpend6": "{1} ingin kembali bersama Anda. Jika Anda memutuskan untuk melakukan rekonsiliasi, semua data Anda sebelumnya akan dipulihkan.",
- "reconcileInvitationTips": "*Jika pihak lain menolak undangan CP, koin Anda akan dikembalikan ke dompet Anda.",
- "reconcileInvitation": "Rekonsiliasi Undangan",
- "areYouSureYouWantToSpend4": "mengirim undangan rekonsiliasi kepada pengguna ini?",
- "partWaysTips": "*Jika salah satu pasangan memilih untuk berpisah, akan ada periode jeda selama 7 hari. Selama jangka waktu tersebut, kedua belah pihak dapat memilih untuk melakukan rekonsiliasi, dan semua data akan dipulihkan setelah rekonsiliasi. Jika tidak ada rekonsiliasi yang dipilih pada akhir periode 7 hari, data pasangan tersebut akan dihapus.",
- "areYouSureYouWantToPartWaysWithYourCP": "Apakah Anda yakin ingin berpisah dengan CP Anda?",
- "timeSpentTogether": "Waktu yang dihabiskan bersama: {1} hari",
- "firstDay": "Hari pertama:{1}",
- "numberOfMyCPs": "Jumlah CP saya:({1}/{2})",
- "props": "Alat peraga",
- "win": "Menang",
- "dice": "Dadu",
- "rps": "RPS",
- "operationFail": "Operasi gagal.",
- "enterRoomFailedRetry": "Gagal masuk ruangan. Silakan coba lagi.",
- "voiceConnectionFailedRetry": "Sambungan suara gagal. Silakan coba lagi.",
- "likedYourComment": "Menyukai komentar Anda.",
- "doYouWantToKeepTheDraft": "Apakah Anda ingin menyimpan drafnya?",
- "operationsAreTooFrequent": "Operasi terlalu sering",
- "luckNumber": "Nomor Keberuntungan",
- "relationShip": "Hubungan",
- "couple": "Pasangan {1}:",
- "couple2": "Pasangan",
- "reject": "Menolak",
- "accept": "Menerima",
- "noMatchedCP": "Tidak ada CP yang cocok",
- "inviteYouToBecomeBD": "Mengundang Anda untuk menjadi BD.",
- "adminInviteRechargeAgent": "Mengundang Anda untuk menjadi agen Isi Ulang.",
- "confirmAcceptTheInvitation": "Konfirmasikan untuk menerima undangan?",
- "confirmDeclineTheInvitation": "Konfirmasi penolakan undangan?",
- "cpInviteRejectedByOther": "Pihak lain menolak menjadi {1}.",
- "closeFriendInvitationTitle": "Undangan dari Teman Dekat",
- "closeFriendInvitationSubtitle": "Klik untuk melihat undangan",
- "closeFriendInvitationExpired": "Undangan untuk menambah teman dekat telah kedaluwarsa. Tindakan ini tidak dapat dilakukan.",
- "closeFriendRequestDeclined": "Permintaan pertemanan ditolak",
- "closeFriendsHaveBecome": "Telah menjadi teman dekat",
- "otherAcceptedCloseFriendInvite": "Pihak lain menerima undangan teman dekat Anda.",
- "otherDeclinedCloseFriendInvite": "Pihak lain menolak undangan teman dekat Anda.",
- "youAcceptedCloseFriendInvite": "Anda menerima undangan teman dekat pihak lain.",
- "youDeclinedCloseFriendInvite": "Anda menolak undangan teman dekat pihak lain.",
- "closeFriendInviteTimedOut": "Batas waktu undangan teman dekat telah habis.",
- "cpDismissConfirmMessage": "Menghapus hubungan ini akan dikenakan biaya {1} koin. Konfirmasikan penghapusan?",
- "cpRelationCp": "CP",
- "cpRelationBrother": "Saudara laki-laki",
- "cpRelationSister": "Saudari",
- "host": "Tuan rumah",
- "following": "Mengikuti",
- "agent": "Agen",
- "approved": "Disetujui",
- "approvedWithinOneMinute": "Disetujui dan ditampilkan dalam waktu 1 menit setelah penyerahan",
- "onlineUsers": "Pengguna Daring ({1}/{2}):",
- "applyToJoin": "Ajukan permohonan untuk bergabung",
- "supporterList": "Daftar Pendukung",
- "hostList": "Daftar Tuan Rumah",
- "upToAdmins": "Hingga {1} Admin",
- "upToMembers": "Hingga {1} Anggota",
- "levelPrivileges": "Hak istimewa tingkat",
- "ra": "RA",
- "roomAnnouncement": "Pengumuman Kamar",
- "help": "Membantu",
- "rejected": "Ditolak",
- "boxContributeTips": "Investasi telah dilakukan hari ini. Tolong jangan berinvestasi lagi.",
- "bd": "BD",
- "coupon": "Kupon",
- "search": "Mencari",
- "get": "Mendapatkan",
- "inRocket": "Di dalam roket",
- "roomRocketHelpTips": "1. Mengirim hadiah di dalam ruangan meningkatkan energi roket. *1 hadiah koin emas = 1 titik energi roket; hadiah keberuntungan meningkatkan energi roket sebesar 4% dari nilai koin emas hadiah.\n2. Setelah energi roket terisi penuh, ruangan dapat meluncurkan roket. Hadiah akan didistribusikan secara otomatis setelah peluncuran.\n3. Level roket yang berbeda menawarkan hadiah yang berbeda.\n4. Saat roket diluncurkan, semua pengguna di dalam ruangan dapat mengklaim hadiah roket.\n5. Energi roket disetel ulang pada pukul 00:00 setiap hari.",
- "couponRecord": "Catatan penggunaan kupon",
- "inRoom": "Di Kamar",
- "searchCouponHint": "Cari Kupon",
- "giftCounter": "Penghitung hadiah",
- "bDLeaderInviteYouToBecomeBDLeader": "Mengundang Anda untuk menjadi BDLeader",
- "wins": "menang",
- "inviteYouToBecomeHost": "Mengundang Anda untuk menjadi tuan rumah.",
- "friends": "Teman-teman",
- "deleteConversationTips": "Apakah Anda yakin ingin menghapus riwayat obrolan dengan pengguna ini?",
- "propMessagePrompt": "Prompt pesan prop",
- "inputUserId": "Masukkan ID Pengguna",
- "fromLuckyGifts": "Dari hadiah keberuntungan/ajaib",
- "receive": "Menerima",
- "checkInSuccessful": "Check-in berhasil",
- "sginTips": "Anda akan mendapatkan hadiah saat pertama kali login setiap hari. Jika Anda menghentikan proses login berturut-turut, hadiah akan dihitung ulang dari hari pertama saat Anda login lagi.",
- "popular": "Populer",
- "recommend": "Menyarankan",
- "follow": "Mengikuti",
- "history": "Sejarah",
- "hotRooms": "Kamar Panas",
- "viewMore": "Lihat lebih banyak",
- "noData": "Tidak ada data",
- "users": "Pengguna",
- "rooms": "Kamar",
- "coins": "koin",
- "unread": "Belum dibaca",
- "read": "Membaca",
- "image": "[Gambar]",
- "video": "[Video]",
- "sound": "[Suara]",
- "gift2": "[Hadiah]",
- "clickHereToStartChatting": "Katakan sesuatu.....",
- "receivedAMessage": "[Menerima pesan]",
- "confirmSwitchMicModelTips": "Apakah Anda mengonfirmasi peralihan dalam mode tempat duduk?",
- "number": "Nomor",
- "album": "Album",
- "camera": "Kamera",
- "system": "Sistem",
- "notifcation": "Pemberitahuan",
- "inviteYouToBecomeAgent": "Mengundang Anda untuk menjadi agensi.",
- "myMusic": "Musik Saya",
- "add": "Menambahkan",
- "pullToLoadMore": "Tarik untuk memuat lebih banyak",
- "loadingFailedClickToRetry": "Gagal memuat, klik untuk mencoba lagi",
- "releaseToLoadMore": "Lepaskan untuk memuat lebih banyak",
- "haveMyLimits": "---Aku punya batasku.---",
- "music": "Musik",
- "free": "Bebas",
- "charm": "Pesona hadiah",
- "start": "Awal",
- "stop": "Berhenti",
- "chats": "Obrolan",
- "refuse": "Menolak",
- "agree": "Setuju",
- "thisFeatureIsCurrentlyUnavailable": "Fitur ini saat ini tidak tersedia.",
- "pleaseSelectTheRecipient": "Silakan pilih penerima.",
- "searchMemberIdHint": "Silakan masukkan ID anggota",
- "unclaimedRedEnvelopes": "Amplop merah yang tidak diambil akan dikembalikan dalam 24 jam.",
- "redEnvelopeNotYetClaimed": "Amplop merah belum diklaim.",
- "redEnvelopeAmount": "Jumlah amplop merah: {1} koin",
- "sentARedEnvelope": "mengirim amplop merah.",
- "theRedEnvelopeHasExpired": "Amplop merah sudah habis masa berlakunya.",
- "joinRequest": "Permintaan Gabung",
- "scrollToTheBottom": "Gulir ke bawah",
- "gameRules": "Aturan Permainan:",
- "charmGameRulesTips": "Aktifkan dasbor untuk menampilkan koin hadiah yang diterima semua pengguna di mikrofon. 1 koin = 1 skor (Hadiah keberuntungan: 1 koin = skor 0,04).",
- "inputYourOldPassword": "Masukkan kata sandi lama Anda",
- "enterYourOldPassword": "Masukkan kata sandi lama Anda",
- "setYourPassword": "Tetapkan kata sandi Anda",
- "enterYourNewPassword": "Masukkan kata sandi baru Anda",
- "confirmYourPassword": "Konfirmasikan kata sandi Anda",
- "theTwoPasswordsDoNotMatch": "Kedua kata sandi tidak cocok.",
- "resetLoginPasswordtTips2": "Kata sandi harus terdiri dari 8-16 karakter dan harus berupa kombinasi huruf besar dan kecil bahasa Inggris serta angka (bukan hanya angka).",
- "resetLoginPassword": "Atur Ulang Kata Sandi Masuk",
- "resetLoginPasswordtTips1": "Masuk dengan ID pengguna atau ID cantik Anda. Lebih aman masuk dengan akun yumi.",
- "localMusic": "Musik Lokal",
- "setLoginPassword": "Tetapkan Kata Sandi Masuk",
- "confirmSwitchMicThemeTips": "Apakah Anda mengonfirmasi peralihan gaya kursi?",
- "micTheme": "Tema Mikrofon",
- "classicMic": "Klasik {1} Mikrofon",
- "yesterday": "Kemarin {1}",
- "monday": "Senin {1}",
- "tuesday": "Selasa {1}",
- "wednesday": "Rabu {1}",
- "thursday": "Kamis {1}",
- "friday": "Jumat {1}",
- "saturday": "Sabtu {1}",
- "sunday": "Minggu {1}",
- "acceptedYour": "{1} menerima Anda ",
- "youAccepted": "Anda menyetujui {1} ",
- "openRedPackDialogTip": "Amplop merah",
- "micManagement": "Manajemen Mikrofon",
- "bdCenter": "Pusat BD",
- "rechargeAgency": "Badan Isi Ulang",
- "adminCenter": "Pusat Admin",
- "managerCenter": "Pusat Manajer",
- "goldList": "Daftar Emas",
- "followList": "Ikuti Daftar",
- "fansList": "Daftar Penggemar",
- "daily": "Sehari-hari",
- "female": "Perempuan",
- "male": "Pria",
- "identity": "Identitas",
- "adjust": "Menyesuaikan",
- "warning": "Peringatan",
- "screenshotTips": "Tangkapan Layar (Hingga 3)",
- "roomNotice": "Pemberitahuan kamar",
- "roomTheme": "Tema kamar",
- "description": "Keterangan:",
- "inputDesHint": "Mohon jelaskan masalahnya sedetail mungkin agar kami dapat memahami dan menyelesaikannya.",
- "roomProfilePicture": "Gambar profil kamar",
- "userProfilePicture": "Gambar profil pengguna",
- "userName": "Nama belakang",
- "pleaseSelectTheTypeToProcess": "Silakan pilih jenis yang akan diproses:",
- "roomEditing": "Pengeditan Ruangan",
- "setAccount": "Tetapkan Akun",
- "userEditing": "Pengeditan Pengguna",
- "enterTheUserId": "Masukkan ID pengguna",
- "enterTheRoomId": "Masukkan ID ruangan",
- "deleteAccount": "Hapus Akun",
- "becomeAgent": "Menjadi agen",
- "enterNickname": "Masukkan Nama Panggilan",
- "enterInvitationCode": "masukkan kode undangan",
- "selectYourCountry": "Pilih negara Anda",
- "inviteCode": "Kode Undangan",
- "magic": "Sihir",
- "luckGiftSpecialEffects": "Efek animasi hadiah keberuntungan",
- "theVideoSizeCannotExceed": "Ukuran video tidak boleh melebihi 50M",
- "weekly": "Mingguan",
- "customizedGiftRulesContent": "Cara mendapatkan hadiah khusus saya:\n1.Tentukan apakah pengguna berhak menerima hadiah khusus berdasarkan tingkat kekayaan mereka saat ini.\n(1) Ketika tingkat kekayaan pengguna ≥35: Pengguna dapat mengunggah video untuk hadiah khusus satu kali. Kami akan menggunakan gambar profil pengguna saat ini sebagai gambar hadiah dan video yang disediakan sebagai efek hadiah pada \"Disesuaikan\". Produksi akan memakan waktu beberapa saat, dan kami akan memberi tahu Anda segera setelah tersedia di toko.\n(2) Ketika tingkat kekayaan pengguna ≥45: Pengguna dapat mengunggah video untuk hadiah khusus satu kali. Kami akan menggunakan gambar profil pengguna saat ini sebagai gambar hadiah dan video yang disediakan sebagai efek hadiah pada \"Disesuaikan\". Produksi akan memakan waktu beberapa saat, dan kami akan memberi tahu Anda segera setelah tersedia di toko.\n2.Anda dapat berpartisipasi dalam aktivitas tertentu di aplikasi dan, setelah memenuhi kriteria, menghubungi kami melalui bagian \"Pesan\" → \"Hubungi kami\". Berikan tangkapan layar aktivitas dan video yang ingin Anda gunakan untuk hadiah khusus. Kami akan menggunakan gambar profil Anda saat ini sebagai gambar hadiah dan video yang disediakan sebagai efek hadiah, lalu mencantumkannya di bawah \"Disesuaikan\". Produksi akan memakan waktu beberapa saat, dan kami akan memberi tahu Anda segera setelah tersedia di toko.\nPeriode Validitas Hadiah yang Disesuaikan & Cara Memperpanjangnya:\nHadiah eksklusif yang disesuaikan akan tersedia di rak untuk masa berlaku 30 hari. Untuk memungkinkan pengguna yang berpengaruh dan menginspirasi untuk terus menikmati pengalaman baru ini dan menampilkan gaya pribadi mereka, pengguna yang memiliki hadiah khusus dapat memperpanjang waktu penyimpanan semua hadiah khusus mereka selama 30 hari dengan mengisi ulang $500 pada bulan tertentu.",
- "customizedGiftRules": "Aturan Hadiah yang Disesuaikan",
- "rulesUpload": "Aturan & Unggah",
- "monthly": "Bulanan",
- "message": "Pesan",
- "clearCache": "Hapus Tembolok",
- "customized": "Disesuaikan",
- "searchInputHint": "Masukkan nomor rekening/kamar",
- "kickRoomTips": "Anda telah diusir dari ruangan.",
- "roomKickedEntryBlockedTips": "Anda telah dikeluarkan dari ruangan oleh admin dan tidak dapat masuk untuk saat ini.",
- "joinRoomTips": "Bergabung dengan ruangan!",
- "roomSetting": "Pengaturan Ruangan",
- "roomDetails": "Detail Kamar",
- "systemRoomTips": "Bersikaplah sopan dan hormat. Konten pornografi atau vulgar apa pun dilarang keras di yumi. Setelah ditemukan, akun tersebut akan diblokir secara permanen. Harap secara sadar mematuhi peraturan platform yumi.",
- "copiedToClipboard": "Disalin ke papan klip",
- "recharge": "Isi ulang",
- "receivedFromALuckyGift": "Diterima dari hadiah keberuntungan/ajaib.",
- "followedYou": "Mengikutimu",
- "agentCenter": "Pusat Agensi",
- "areYouSureYouWantToClearLocalCache": "Apakah Anda yakin ingin menghapus cache lokal?",
- "clearMessage": "Hapus pesan layar",
- "report": "Laporan",
- "coins3": "Koin",
- "giftSpecialEffects": "Hadiahkan efek khusus",
- "basicFeatures": "Fitur Dasar",
- "task": "Tugas",
- "importantReminder": "Pengingat Penting",
- "entryVehicleAnimation": "Animasi kendaraan masuk",
- "floatingAnimationInGlobal": "Animasi mengambang secara global",
- "broadcast": "Siaran",
- "broadcastDisplay": "Tampilan siaran",
- "broadcastAllPlaces": "Semua tempat",
- "broadcastRoomOnly": "Di kamar",
- "broadcastOff": "Tutup siaran",
- "entryVehicleAnimation2": "Pengguna dengan hak istimewa VIP4 atau lebih tinggi dapat menggunakan fungsi ini untuk menonaktifkan animasi kendaraan.",
- "dailyTasks": "Tugas Harian",
- "exclusiveForNewcomers": "Eksklusif Untuk Pendatang Baru",
- "resetsDailyAtMidnight": "Direset setiap hari pada tengah malam",
- "limitedToOneTime": "Terbatas satu kali saja",
- "useMicrophoneForOneMin": "Gunakan mikrofon selama 1 menit",
- "taskRequirement": "Persyaratan",
- "taskProgress": "Kemajuan",
- "rewardClaimedSuccessfully": "Hadiah berhasil diklaim",
- "rewardClaimFailed": "Klaim hadiah gagal",
- "enterRoomConfirmTips": "Apakah Anda yakin ingin memasuki ruangan?",
- "followSucc": "Diikuti dengan sukses",
- "goldListort": "Daftar Emas",
- "rechargeList": "Daftar Isi Ulang",
- "edit": "Sunting",
- "swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*Tips: Gesek ke kiri pada area layar mengambang untuk menutupnya dengan cepat.",
- "enterThisVoiceChatRoom": "Masuk ke ruang obrolan suara ini?",
- "go": "Pergi",
- "done": "Selesai",
- "improvementTasks": "Tugas Peningkatan",
- "save": "Menyimpan",
- "kickPrevention": "Pencegahan tendangan",
- "freeChatSpeak": "Obrolan & Bicara Gratis",
- "nickName": "Nama panggilan",
- "gender": "Jenis kelamin",
- "unFollow": "Berhenti mengikuti",
- "days": "hari",
- "permanent": "Permanen",
- "country": "Negara",
- "birthday": "Hari ulang tahun",
- "man": "Pria",
- "woman": "Wanita",
- "apple": "Apel",
- "google": "Google",
- "startVoiceParty": "Mulai pesta suara!",
- "enterRoomTips": "{1} Masuk ke ruangan",
- "roomName": "Nama Kamar",
- "roomMember": "Anggota Kamar",
- "pleaseSelectYourCountry": "Silakan pilih negara Anda.",
- "pleaseSelectYourGender": "Silakan pilih jenis kelamin Anda.",
- "pleaseEnterNickname": "Silakan masukkan nama panggilan.",
- "countryRegion": "Negara & Wilayah",
- "dateOfBirth": "Tanggal lahir",
- "mute": "Bisu",
- "exit": "KELUAR",
- "mysteriousInvisibility": "Gaib yang misterius",
- "antiBlock": "Anti-Blok",
- "privateChat": "Obrolan Pribadi",
- "storeDiscount": "Diskon Toko Diskon {1}",
- "membershipFreeChatSpeak": "Obrolan & Bicara bebas keanggotaan",
- "priorityRoomSorting": "Penyortiran Kamar Prioritas",
- "userColoredID": "ID Berwarna Pengguna",
- "pleaseSelectaItem": "Silakan pilih item",
- "areYouRureRoRecharge": "Apakah Anda yakin ingin mengisi ulang?",
- "mInimize": "Menyimpan",
- "everyone": "Setiap orang",
- "shop": "Toko",
- "roomOwner": "Pemilik Kamar",
- "dailyTaskRewardBonus": "Bonus Hadiah Tugas Harian ({1} XP)",
- "userLevelXPBoost": "Peningkatan XP Tingkat Pengguna ({1} XP)",
- "andAboveUsers": "{1} Dan Pengguna Di Atas",
- "privileges": "{1} Hak istimewa",
- "basicPermissions": "Izin dasar",
- "hostCenter": "Pusat Tuan Rumah",
- "allOnMicrophone": "Semuanya menggunakan mikrofon",
- "usersOnMicrophone": "Pengguna di mikrofon",
- "allInTheRoom": "Semua di kamar",
- "send": "Mengirim",
- "crop": "Tanaman",
- "goToUpgrade": "Pergi untuk meningkatkan",
- "exclusiveEmojiWillBeReleasedAfterBecoming": "Emoji eksklusif akan dirilis setelah menjadi",
- "preventBeingBlocked": "Mencegah Diblokir",
- "enableRankIncognitoMode": "Aktifkan Mode Penyamaran Peringkat",
- "avoidBeingKicked": "Hindari Ditendang",
- "finish": "Menyelesaikan",
- "takeTheMic": "Ambil mikrofonnya",
- "openTheMic": "Buka mikrofon",
- "muteTheMic": "Matikan mikrofon",
- "unlockTheMic": "Buka kunci mikrofon",
- "leavelTheMic": "Tinggalkan mikrofon",
- "lockTheMic": "Kunci mikrofon",
- "removeTheMic": "Hapus mikrofon",
- "inviteToTheMicrophone": "Undang ke mikrofon",
- "openUserProfleCard": "Buka kartu profil pengguna",
- "obtain": "memperoleh",
- "win2": "Menangkan {1}",
- "backTheRoom": "Ruang belakang",
- "toConsume": "Untuk mengkonsumsi",
- "howToUpgrade": "Bagaimana cara meningkatkannya?",
- "spendCoinsToGainExperiencePoints": "Habiskan Koin untuk mendapatkan poin pengalaman",
- "richLevelUpgradeAnswer": "Biarkan kekayaan Anda mengalahkan segalanya",
- "charmLevelUpgradeAnswer": "Buat orang lain tunduk pada pesona Anda",
- "higherLevelFancierAvatarFrame": "Tingkat yang lebih tinggi, lencana/bingkai avatar yang lebih menarik",
- "all": "Semua",
- "gift": "Hadiah",
- "chat": "Mengobrol",
- "owner": "Pemilik",
- "store": "Toko",
- "admin": "Admin",
- "member": "Anggota",
- "guest": "Tamu",
- "submit": "Kirim",
- "claim": "Mengeklaim",
- "claimed": "Diklaim",
- "complete": "Menyelesaikan",
- "shareTo": "Bagikan ke",
- "copyLink": "Salin Tautan",
- "faceBook": "Facebook",
- "whatsApp": "ada apa",
- "snapChat": "Snapchat",
- "taskNamePersonalGameConsume": "Pengeluaran Game",
- "taskNameRoomNewMember": "Anggota Ruang Baru",
- "taskNameRoomOwnerSendRedPacket": "Pemilik Kamar mengirimkan satu paket merah",
- "taskNameRoomOwnerSendGiftUser": "Pemilik Kamar Mengirim Hadiah",
- "taskNameRoomMicUser120Min": "Anggota dengan 120+ menit menggunakan mikrofon",
- "taskNameRoomMicUser60Min": "Anggota dengan 60+ menit menggunakan mikrofon",
- "taskNameRoomMicUser30Min": "Anggota dengan 30+ menit menggunakan mikrofon",
- "taskNamePersonalSendGift": "Kirim Hadiah ke Pengguna",
- "taskNameRoomOnlineUserCount": "Anggota ruang online",
- "taskNameRoomOwnerInviteMic": "Undang anggota ke Mic",
- "taskNameRoomUserSendGiftGold": "Koin yang diberikan oleh anggota",
- "taskNameRoomOwnerSendGiftGold": "Koin yang diberikan oleh pemilik kamar",
- "taskNamePersonalMagicGiftGold": "Koin Dimenangkan dengan Mengirim Hadiah Ajaib",
- "taskNamePersonalLuckyGiftGold": "Koin Dimenangkan dengan Mengirim Hadiah Keberuntungan",
- "taskNameRoomOwnerMicTime": "Pemilik Kamar menyalakan mikrofon di dalam kamar",
- "taskNamePersonalActiveInRoom": "Jadilah Aktif di Ruangan Orang Lain",
- "taskNamePersonalMicInRoom": "Ayo Mikrofon",
- "dailyCoinBonanzaRulesDetail": "1. Tugas Pribadi Harian dan Tugas Pemilik Ruangan Harian dapat diselesaikan satu kali per pengguna per hari. Tugas diatur ulang pada 00:00:00 waktu Saudi.\n2. Tugas pribadi sehari-hari hanya dapat diselesaikan di ruangan orang lain; tugas pemilik ruangan hanya dapat diselesaikan di ruangan Anda sendiri.\n3. Tugas pemberian hadiah hanya menghitung hadiah yang dikirim di ruangan, bukan hadiah yang dikirim di feed.\n4. Jika beberapa akun dibuat menggunakan perangkat yang sama atau kartu SIM yang sama dengan cara apa pun, hadiah untuk semua tugas hanya dapat diklaim satu kali.",
- "dailyCoinBonanzaRules": "Aturan Bonanza Koin Harian",
- "roomOwnerTasks": "Tugas Pemilik Ruangan",
- "personalTasks": "Tugas Pribadi",
- "noPromptsToday": "Tidak ada tugas hari ini.",
- "getPaidToRefer": "Dapatkan Bayaran untuk Merujuk",
- "membershipFee": "Biaya Keanggotaan",
- "membershipFeeTips1": "Silakan tentukan biaya keanggotaan untuk kamar Anda. Pengguna dapat bergabung dengan kamar Anda dengan membayar biayanya.",
- "membershipFeeTips2": "Emas yang dibutuhkan pengguna untuk menjadi anggota ruangan. Pemilik ruangan akan mendapatkan 50% emas.",
- "freePrice": "Biaya: 0-10.000",
- "touristsSendText": "Turis mengirim SMS",
- "touristsTakeToTheMic": "Wisatawan mendengarkan mikrofon",
- "theMembershipFee": "Biaya keanggotaan",
- "theModificationsMade": "Modifikasi yang dilakukan kali ini tidak akan disimpan setelah keluar",
- "viewFrame": "Lihat Bingkai",
- "enterRoomName": "Masukkan nama ruangan",
- "headdress": "Bingkai",
- "mountains": "Kendaraan",
- "purchaseIsSuccessful": "Pembelian berhasil",
- "buy": "Membeli",
- "followed": "Diikuti",
- "follow2": "Ikuti:{1}",
- "fans2": "Penggemar:{1}",
- "vistors2": "Pengunjung:{1}",
- "personal2": "Pribadi:",
- "conntinue": "Melanjutkan",
- "confirmBuyTips": "Apakah Anda yakin ingin membeli?",
- "purchase": "Pembelian",
- "setRoomPassword": "Tetapkan kata sandi ruangan",
- "inputRoomPassword": "Masukkan kata sandi ruangan",
- "enter": "Memasuki",
- "deleteCommentTips": "Apakah Anda yakin ingin menghapus komentar ini?",
- "deleteSuccessful": "Penghapusan berhasil!",
- "itemsLeft": "Barang tersisa",
- "password": "Kata sandi",
- "replySucc": "Balasan berhasil",
- "comment": "Komentar",
- "showMore": "Tampilkan lebih banyak",
- "showLess": "Tampilkan lebih sedikit",
- "enterPassword": "Masukkan Kata Sandi",
- "enterAccount": "Masuk ke Akun",
- "logIn": "Masuk",
- "saySomething": "Katakan sesuatu...",
- "sayHi": "Ucapkan salam..",
- "pleaseChatFfriendly": "Silakan ngobrol dengan ramah",
- "unLockTheRoom": "Buka Kunci Ruangan",
- "operationSuccessful": "Operasinya berhasil.",
- "adminByHomeowner": "ditetapkan sebagai administrator oleh pemilik rumah.",
- "memberByHomeowner": "ditetapkan sebagai anggota oleh pemilik rumah.",
- "touristByHomeowner": "ditetapkan sebagai turis oleh pemilik rumah.",
- "becomeHost": "Lamar Untuk Menjadi Tuan Rumah",
- "superFans": "Penggemar super:",
- "setUpAnIdentity": "Siapkan identitas",
- "kickedOutOfRoom": "Ditendang keluar dari ruangan",
- "knapsack": "ransel",
- "bdLeader": "Pemimpin BD",
- "picture": "Gambar",
- "theImageSizeCannotExceed": "Ukuran gambar tidak boleh melebihi 4M",
- "activity": "Aktivitas",
- "alreadyAnAdministrator": "Sudah menjadi administrator",
- "alreadyAnMember": "Sudah menjadi anggota",
- "alreadyAnTourist": "Sudah menjadi turis",
- "touristsCannotSendMessages": "Wisatawan tidak dapat mengirim pesan",
- "touristsAreNotAllowedToGoOnTheMic": "Wisatawan tidak diperbolehkan menggunakan mikrofon",
- "lockTheRoom": "Kunci Ruangan",
- "special": "Spesial",
- "visitorList": "Daftar Pengunjung",
- "successfulWear": "Pakaian yang sukses",
- "confirmUnUseTips": "Apakah Anda mengonfirmasi untuk menghapusnya?",
- "custom": "Kebiasaan",
- "customBackground": "Latar Belakang Kustom",
- "example": "Contoh",
- "endPreview": "Akhiri Pratinjau",
- "selectAgain": "Pilih lagi",
- "myItems": "Barang saya",
- "use": "Menggunakan",
- "unUse": "Buka perlengkapan",
- "renewal": "Pembaruan",
- "wallet": "Dompet",
- "profile": "Profil",
- "giftwall": "dinding hadiah",
- "announcement": "Pengumuman",
- "blockedList": "Daftar yang diblokir",
- "country2": "Negara:",
- "sendTo": "Kirim ke",
- "credits": "Kredit: {1}",
- "successfullyUnloaded": "Berhasil dibongkar",
- "expired": "Kedaluwarsa",
- "day": "Hari",
- "inUse": "Sedang digunakan",
- "staticOrGifImage": "Gambar statis / GIF",
- "confirmUseTips": "Apakah Anda ingin mengonfirmasi penggunaannya?",
- "pleaseUploadUserAvatar": "Silakan unggah avatar.",
- "joinMemberTips": "Jika Anda adalah pengunjung ruangan, Anda tidak dapat mengambil mikrofon.",
- "giftGivingSuccessful": "Pemberian hadiah berhasil.",
- "theAccountPasswordCannotBeEmpty": "Akun atau kata sandi tidak boleh kosong.",
- "invitesYouToTheMicrophone": "{1} mengundang Anda ke mikrofon",
- "english": "Bahasa inggris",
- "chinese": "Cina",
- "arabic": "Arab",
- "darkMode": "Mode Gelap",
- "lightMode": "Modus Cahaya",
- "systemDefault": "Bawaan Sistem",
- "pleaseGetOnTheMicFirst": "Silakan nyalakan mikrofonnya terlebih dahulu.",
- "roomMusicAddMusic": "Tambahkan Musik",
- "roomMusicSelectAll": "Pilih semua",
- "roomMusicAddedCount": "Menambahkan {1} lagu",
- "roomMusicEmpty": "Tidak ada musik",
- "roomMusicScanFromPhone": "Pindai dari ponsel",
- "roomMusicTotalSongs": "Jumlah: {1} lagu",
- "roomMusicNotFound": "Tidak ada musik yang ditemukan",
- "roomMusicDeleteConfirm": "Hapus musik ini?",
- "roomMusicDeleted": "Musik dihapus",
- "roomMusicListMode": "Mainkan secara berurutan",
- "roomMusicShuffleMode": "Putar acak",
- "roomMusicSingleMode": "Lingkaran tunggal",
- "duration2": "Durasi:{1}",
- "roomRocketNoteTitle": "Catatan",
- "roomRocketNoteSectionTitle": "Setiap kali VIP",
- "roomRocketNoteSectionBody": "1. Setiap kali level VIP dibeli, level tersebut berlaku selama 30 hari, dan dapat diperpanjang secara manual setelah habis masa berlakunya.",
- "roomRocketRewardEmptyText": "Tidak ada imbalan",
- "roomRocketRecordEmptyText": "Tidak ada catatan hadiah",
- "launchRocket": "Luncurkan Roket",
- "roomRocketFuelFullNotice": "Bahan bakar roket sudah penuh! Diluncurkan dalam waktu {1} detik!"
-}
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index d03fab7..0b12671 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -510,7 +510,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = 1;
VALIDATE_PRODUCT = YES;
};
name = Profile;
@@ -524,7 +524,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1631;
+ CURRENT_PROJECT_VERSION = 102;
DEVELOPMENT_TEAM = S9YG87C297;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -533,7 +533,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.6.3;
+ MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -647,7 +647,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
@@ -701,7 +701,7 @@
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = 1;
VALIDATE_PRODUCT = YES;
};
name = Release;
@@ -715,7 +715,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1631;
+ CURRENT_PROJECT_VERSION = 102;
DEVELOPMENT_TEAM = S9YG87C297;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -724,7 +724,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.6.3;
+ MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -744,7 +744,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 1631;
+ CURRENT_PROJECT_VERSION = 102;
DEVELOPMENT_TEAM = S9YG87C297;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -753,7 +753,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.6.3;
+ MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 76689b7..3d176d0 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -31,8 +31,8 @@
yumi
CFBundlePackageType
APPL
- CFBundleShortVersionString
- 1.0.0
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
CFBundleSignature
????
CFBundleURLTypes
@@ -57,8 +57,8 @@
CFBundleVersion
- 100
- LSRequiresIPhoneOS
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
NSCameraUsageDescription
We need access to your camera so you can take a photo and make it a profile picture, or take and share a picture in real time in a voice chat room.
diff --git a/lib/app/config/configs/sc_variant1_config.dart b/lib/app/config/configs/sc_variant1_config.dart
index e4502ee..c31ecf2 100644
--- a/lib/app/config/configs/sc_variant1_config.dart
+++ b/lib/app/config/configs/sc_variant1_config.dart
@@ -16,7 +16,7 @@ class SCVariant1Config implements AppConfig {
@override
String get apiHost => const String.fromEnvironment(
'API_HOST',
- defaultValue: 'https://jvapi.haiyihy.com/',
+ defaultValue: 'http://192.168.110.64:1100/',
); // 默认连本地接口,本地调试可通过 --dart-define=API_HOST 覆盖,线上“https://jvapi.haiyihy.com/”
@override
diff --git a/lib/app_localizations.dart b/lib/app_localizations.dart
index e08ca12..6c03225 100644
--- a/lib/app_localizations.dart
+++ b/lib/app_localizations.dart
@@ -16,8 +16,6 @@ class SCAppLocalizations {
'it',
'pt',
'fr',
- 'id',
- 'es',
];
static const List supportedLocales = [
@@ -28,8 +26,6 @@ class SCAppLocalizations {
Locale('it', ''),
Locale('pt', ''),
Locale('fr', ''),
- Locale('id', ''),
- Locale('es', ''),
];
static bool isSupportedLanguageCode(String languageCode) {
diff --git a/lib/debug/lucky_gift_global_floating_preview.dart b/lib/debug/lucky_gift_global_floating_preview.dart
deleted file mode 100644
index 43e5c18..0000000
--- a/lib/debug/lucky_gift_global_floating_preview.dart
+++ /dev/null
@@ -1,180 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_screenutil/flutter_screenutil.dart';
-import 'package:pag/pag.dart';
-import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart';
-import 'package:yumi/ui_kit/widgets/room/floating/floating_lucky_gift_global_screen_widget.dart';
-
-void main() {
- runApp(const LuckyGiftGlobalFloatingPreviewApp());
-}
-
-class LuckyGiftGlobalFloatingPreviewApp extends StatelessWidget {
- const LuckyGiftGlobalFloatingPreviewApp({super.key});
-
- @override
- Widget build(BuildContext context) {
- return ScreenUtilInit(
- designSize: const Size(375, 812),
- minTextAdapt: true,
- builder: (_, child) {
- return MaterialApp(
- debugShowCheckedModeBanner: false,
- home: const _LuckyGiftGlobalFloatingPreviewPage(),
- );
- },
- );
- }
-}
-
-class _LuckyGiftGlobalFloatingPreviewPage extends StatelessWidget {
- const _LuckyGiftGlobalFloatingPreviewPage();
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Colors.black,
- body: Stack(
- children: [
- Positioned.fill(
- child: Image.asset(_backgroundAsset, fit: BoxFit.cover),
- ),
- Positioned(
- left: 0,
- top: 116.w,
- child: FloatingLuckyGiftGlobalScreenWidget(
- debugDisableAutoSlide: true,
- message: SCFloatingMessage(
- type: FloatingLuckyGiftGlobalScreenWidget.floatingType,
- roomId: '1002900',
- userName: 'Bajul',
- userAvatarUrl: '',
- coins: 1000,
- multiple: 10,
- ),
- onAnimationCompleted: () {},
- ),
- ),
- Positioned(left: 0, top: 250.w, child: const _PagLayerScanner()),
- ],
- ),
- );
- }
-}
-
-const _backgroundAsset = 'sc_images/room/background_examples/bg_example_1.png';
-
-class _PagLayerScanner extends StatefulWidget {
- const _PagLayerScanner();
-
- @override
- State<_PagLayerScanner> createState() => _PagLayerScannerState();
-}
-
-class _PagLayerScannerState extends State<_PagLayerScanner> {
- final _pagKey = GlobalKey();
- final _foundLayers = {};
- String _summary = 'loading PAG...';
-
- @override
- Widget build(BuildContext context) {
- return SizedBox(
- width: FloatingLuckyGiftGlobalScreenWidget.bannerWidth.w,
- height: 160.w,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SizedBox(
- width: FloatingLuckyGiftGlobalScreenWidget.bannerWidth.w,
- height: FloatingLuckyGiftGlobalScreenWidget.bannerHeight.w,
- child: PAGView.asset(
- FloatingLuckyGiftGlobalScreenWidget.backgroundPagAssetPath,
- key: _pagKey,
- width: FloatingLuckyGiftGlobalScreenWidget.bannerWidth.w,
- height: FloatingLuckyGiftGlobalScreenWidget.bannerHeight.w,
- autoPlay: false,
- initProgress: 0.35,
- repeatCount: PAGView.REPEAT_COUNT_LOOP,
- onInit: _scanLayers,
- ),
- ),
- Container(
- width: FloatingLuckyGiftGlobalScreenWidget.bannerWidth.w,
- padding: EdgeInsets.all(6.w),
- color: Colors.black.withValues(alpha: 0.65),
- child: Text(
- _summary,
- maxLines: 4,
- overflow: TextOverflow.ellipsis,
- textScaler: TextScaler.noScaling,
- style: TextStyle(
- color: Colors.white,
- fontSize: 9.sp,
- decoration: TextDecoration.none,
- ),
- ),
- ),
- ],
- ),
- );
- }
-
- Future _scanLayers() async {
- await Future.delayed(const Duration(milliseconds: 200));
- final state = _pagKey.currentState;
- if (state == null) {
- return;
- }
- final rawWidth = state.rawWidth;
- final rawHeight = state.rawHeight;
- debugPrint('[LuckyGiftPAG] rawSize=${rawWidth}x$rawHeight');
- if (rawWidth <= 0 || rawHeight <= 0) {
- return;
- }
- final sampleXs =
- [
- 40,
- 80,
- 120,
- 170,
- 220,
- 280,
- 340,
- 410,
- 480,
- 560,
- 640,
- 710,
- ].where((value) => value < rawWidth).toList();
- final sampleYs =
- [
- 32,
- 58,
- 82,
- 104,
- 128,
- 158,
- ].where((value) => value < rawHeight).toList();
- for (final y in sampleYs) {
- for (final x in sampleXs) {
- final layers = await state.getLayersUnderPoint(x, y);
- if (layers.isEmpty) {
- continue;
- }
- _foundLayers.addAll(layers.where((name) => name.trim().isNotEmpty));
- debugPrint(
- '[LuckyGiftPAG] point=(${x.toStringAsFixed(0)},'
- '${y.toStringAsFixed(0)}) layers=$layers',
- );
- }
- }
- if (!mounted) {
- return;
- }
- setState(() {
- _summary =
- 'raw ${rawWidth.toStringAsFixed(0)}x'
- '${rawHeight.toStringAsFixed(0)} layers: '
- '${_foundLayers.join(', ')}';
- });
- }
-}
diff --git a/lib/modules/chat/message_chat_page.dart b/lib/modules/chat/message_chat_page.dart
index be24c57..f1f6d06 100644
--- a/lib/modules/chat/message_chat_page.dart
+++ b/lib/modules/chat/message_chat_page.dart
@@ -21,7 +21,6 @@ import 'package:yumi/shared/tools/sc_date_utils.dart';
import 'package:yumi/shared/tools/sc_loading_manager.dart';
import 'package:yumi/shared/tools/sc_message_utils.dart';
import 'package:yumi/shared/tools/sc_user_id_utils.dart';
-import 'package:yumi/shared/tools/sc_chat_bubble_message_utils.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
import 'package:yumi/services/audio/rtm_manager.dart';
@@ -57,6 +56,7 @@ import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
import 'package:yumi/shared/tools/sc_keybord_util.dart';
import 'package:yumi/shared/tools/sc_message_notifier.dart';
import 'package:yumi/shared/tools/sc_path_utils.dart';
+import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/services/auth/user_profile_manager.dart';
import 'package:yumi/modules/index/main_route.dart';
@@ -581,14 +581,6 @@ class _SCMessageChatPageState extends State {
// 监听消息扩展更新
onRecvMessageExtensionsChanged: (msgID, extensions) {
for (var ext in extensions) {
- if (ext.extensionKey == scChatBubbleMessageExtensionKey) {
- _MessageItem.updateChatBubbleExtensionCache(
- msgID,
- ext.extensionValue,
- );
- setState(() {});
- continue;
- }
if (ext.extensionKey == "packetID") {
String packetID = ext.extensionValue;
SCMessageUtils.redPacketFutureCache[packetID] =
@@ -993,9 +985,6 @@ class _SCMessageChatPageState extends State {
class _MessageItem extends StatelessWidget {
static bool _isOpeningCpInviteDialog = false;
- static final Map>
- _chatBubbleExtensionFutures = {};
- static final Map _chatBubbleExtensionCache = {};
final V2TimMessage message;
final V2TimMessage? preMessage;
@@ -2634,81 +2623,45 @@ class _MessageItem extends StatelessWidget {
}
Widget _buildTextBubble(String content) {
- final chatBubble = scDecodeChatBubbleFromCloudCustomData(
- message.cloudCustomData,
- );
+ final chatBubble =
+ (message.isSelf ?? false)
+ ? _currentUserChatBubble()
+ : _senderProfile()?.getChatBox();
final chatBubbleUrl = _resolveChatBubbleImageUrl(chatBubble);
if (chatBubbleUrl.isNotEmpty) {
return _buildVipTextBubble(content: content, imageUrl: chatBubbleUrl);
}
- final msgID = message.msgID?.trim();
- if (msgID == null ||
- msgID.isEmpty ||
- message.elemType != MessageElemType.V2TIM_ELEM_TYPE_TEXT) {
- return _buildDefaultTextBubble(content);
- }
- return FutureBuilder(
- future: _chatBubbleFromMessageExtension(message),
- builder: (_, snapshot) {
- final extensionBubbleUrl = _resolveChatBubbleImageUrl(snapshot.data);
- if (extensionBubbleUrl.isNotEmpty) {
- return _buildVipTextBubble(
- content: content,
- imageUrl: extensionBubbleUrl,
- );
- }
- return _buildDefaultTextBubble(content);
- },
- );
+ return _buildDefaultTextBubble(content);
}
- static void updateChatBubbleExtensionCache(
- String msgID,
- String extensionValue,
- ) {
- final id = msgID.trim();
- if (id.isEmpty) {
- return;
+ PropsResources? _currentUserChatBubble() {
+ final currentUser = AccountStorage().getCurrentUser();
+ final memoryBubble = currentUser?.userProfile?.getChatBox();
+ if (_resolveChatBubbleImageUrl(memoryBubble).isNotEmpty) {
+ return memoryBubble;
+ }
+ if (currentUser?.userProfile != null) {
+ return memoryBubble;
+ }
+
+ final userJson = DataPersistence.getCurrentUser();
+ if (userJson.isEmpty) {
+ return memoryBubble;
+ }
+ try {
+ return SocialChatLoginRes.fromJson(
+ jsonDecode(userJson),
+ ).userProfile?.getChatBox();
+ } catch (_) {
+ return memoryBubble;
}
- _chatBubbleExtensionCache[id] = scDecodeChatBubbleSnapshotValue(
- extensionValue,
- );
- _chatBubbleExtensionFutures.remove(id);
}
- Future _chatBubbleFromMessageExtension(
- V2TimMessage message,
- ) {
- final msgID = message.msgID?.trim();
- if (msgID == null || msgID.isEmpty) {
- return Future.value(null);
+ SocialChatUserProfile? _senderProfile() {
+ if (message.isSelf ?? false) {
+ return AccountStorage().getCurrentUser()?.userProfile;
}
- if (_chatBubbleExtensionCache.containsKey(msgID)) {
- return Future.value(_chatBubbleExtensionCache[msgID]);
- }
- return _chatBubbleExtensionFutures.putIfAbsent(msgID, () async {
- if (message.isSupportMessageExtension == false) {
- _chatBubbleExtensionCache[msgID] = null;
- return null;
- }
- try {
- final result = await TencentImSDKPlugin.v2TIMManager
- .getMessageManager()
- .getMessageExtensions(msgID: msgID);
- final extensions = result.data ?? const [];
- for (final extension in extensions) {
- if (extension.extensionKey == scChatBubbleMessageExtensionKey) {
- final bubble = scDecodeChatBubbleSnapshotValue(
- extension.extensionValue,
- );
- _chatBubbleExtensionCache[msgID] = bubble;
- return bubble;
- }
- }
- } catch (_) {}
- _chatBubbleExtensionCache[msgID] = null;
- return null;
- });
+ return friend;
}
Widget _buildDefaultTextBubble(String content) {
diff --git a/lib/modules/gift/gift_page.dart b/lib/modules/gift/gift_page.dart
index df5b1fd..b377174 100644
--- a/lib/modules/gift/gift_page.dart
+++ b/lib/modules/gift/gift_page.dart
@@ -137,6 +137,60 @@ class _GiftPageState extends State with TickerProviderStateMixin {
return 'type=${error.runtimeType} error=$error';
}
+ void _showLocalLuckyGiftFeedback(
+ List acceptUsers, {
+ required SocialChatGiftRes gift,
+ required int quantity,
+ required String giftBatchId,
+ int animationCount = 1,
+ }) {
+ final targetUserIds = _resolveAcceptUserIds(acceptUsers);
+ final rtmProvider = Provider.of(
+ navigatorKey.currentState!.context,
+ listen: false,
+ );
+ final currentUser = AccountStorage().getCurrentUser()?.userProfile;
+ if (currentUser == null) {
+ _giftFxLog(
+ 'local lucky feedback skipped reason=no_current_user giftId=${gift.id}',
+ );
+ return;
+ }
+
+ for (final acceptUser in acceptUsers) {
+ final targetUser = acceptUser.user;
+ if (targetUser == null) {
+ _giftFxLog(
+ 'local lucky feedback skipped reason=no_target_user giftId=${gift.id}',
+ );
+ continue;
+ }
+ _giftFxLog(
+ 'local lucky feedback trigger '
+ 'giftId=${gift.id} '
+ 'giftName=${gift.giftName} '
+ 'toUserId=${targetUser.id} '
+ 'toUserName=${targetUser.userNickname} '
+ 'quantity=$quantity',
+ );
+ rtmProvider.msgFloatingGiftListener?.call(
+ Msg(
+ groupId:
+ rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount,
+ msg: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
+ type: SCRoomMsgType.luckGiftAnimOther,
+ gift: gift,
+ user: currentUser,
+ toUser: targetUser,
+ targetUserIds: targetUserIds,
+ giftBatchId: giftBatchId,
+ number: quantity,
+ customAnimationCount: animationCount,
+ ),
+ );
+ }
+ }
+
void _applyGiftSelection(SocialChatGiftRes? gift, {bool notify = true}) {
checkedGift = gift;
if (gift != null &&
@@ -1547,6 +1601,13 @@ class _GiftPageState extends State with TickerProviderStateMixin {
_showLocalCpInviteWaitingAfterGift(request, request.acceptUsers);
if (request.isLuckyGiftRequest) {
final giftBatchId = _createGiftBatchId(request);
+ _showLocalLuckyGiftFeedback(
+ request.acceptUsers,
+ gift: request.gift,
+ quantity: request.quantity,
+ giftBatchId: giftBatchId,
+ animationCount: request.clickCount,
+ );
await sendLuckGiftAnimOtherMsg(
request.acceptUsers,
gift: request.gift,
@@ -1760,42 +1821,6 @@ class _GiftPageState extends State with TickerProviderStateMixin {
'hasFullScreenEffect=$hasFullScreenEffect '
'effectsEnabled=${SCGlobalConfig.isGiftSpecialEffects}',
);
- if (gift.giftSourceUrl != null && gift.special != null) {
- if (scGiftHasFullScreenEffect(gift.special)) {
- if (SCGlobalConfig.allowsHighCostAnimations &&
- SCGlobalConfig.isGiftSpecialEffects &&
- (rtcProvider?.shouldShowRoomVisualEffects ?? false)) {
- _giftFxLog(
- 'local trigger player play '
- 'path=${gift.giftSourceUrl} '
- 'giftId=${gift.id} '
- 'giftName=${gift.giftName} '
- 'animationCount=$animationCount',
- );
- SCGiftVapSvgaManager().play(gift.giftSourceUrl!);
- } else {
- _giftFxLog(
- 'skip local play because visual effects disabled '
- 'giftId=${gift.id} '
- 'isGiftSpecialEffects=${SCGlobalConfig.isGiftSpecialEffects} '
- 'roomVisible=${rtcProvider?.shouldShowRoomVisualEffects ?? false}',
- );
- }
- } else {
- _giftFxLog(
- 'skip local play because special does not include '
- '${SCGiftType.ANIMSCION.name}/$kSCGiftAnimationSpecialAlias/${SCGiftType.GLOBAL_GIFT.name} '
- 'giftId=${gift.id} special=${gift.special}',
- );
- }
- } else {
- _giftFxLog(
- 'skip local play because giftSourceUrl or special is null '
- 'giftId=${gift.id} '
- 'giftSourceUrl=${gift.giftSourceUrl} '
- 'special=${gift.special}',
- );
- }
Provider.of(
navigatorKey.currentState!.context,
listen: false,
@@ -1861,6 +1886,42 @@ class _GiftPageState extends State with TickerProviderStateMixin {
),
);
}
+ if (gift.giftSourceUrl != null && gift.special != null) {
+ if (scGiftHasFullScreenEffect(gift.special)) {
+ if (SCGlobalConfig.allowsHighCostAnimations &&
+ SCGlobalConfig.isGiftSpecialEffects &&
+ (rtcProvider?.shouldShowRoomVisualEffects ?? false)) {
+ _giftFxLog(
+ 'local trigger player play '
+ 'path=${gift.giftSourceUrl} '
+ 'giftId=${gift.id} '
+ 'giftName=${gift.giftName} '
+ 'animationCount=$animationCount',
+ );
+ SCGiftVapSvgaManager().play(gift.giftSourceUrl!);
+ } else {
+ _giftFxLog(
+ 'skip local play because visual effects disabled '
+ 'giftId=${gift.id} '
+ 'isGiftSpecialEffects=${SCGlobalConfig.isGiftSpecialEffects} '
+ 'roomVisible=${rtcProvider?.shouldShowRoomVisualEffects ?? false}',
+ );
+ }
+ } else {
+ _giftFxLog(
+ 'skip local play because special does not include '
+ '${SCGiftType.ANIMSCION.name}/$kSCGiftAnimationSpecialAlias/${SCGiftType.GLOBAL_GIFT.name} '
+ 'giftId=${gift.id} special=${gift.special}',
+ );
+ }
+ } else {
+ _giftFxLog(
+ 'skip local play because giftSourceUrl or special is null '
+ 'giftId=${gift.id} '
+ 'giftSourceUrl=${gift.giftSourceUrl} '
+ 'special=${gift.special}',
+ );
+ }
}
}
@@ -2065,7 +2126,7 @@ class _GiftPageState extends State with TickerProviderStateMixin {
required String giftBatchId,
int animationCount = 1,
}) async {
- final targetUserIds = _resolveAcceptUserIdentityKeys(acceptUsers);
+ final targetUserIds = _resolveAcceptUserIds(acceptUsers);
final firstTargetUser =
acceptUsers.isNotEmpty ? acceptUsers.first.user : null;
_giftFxLog(
@@ -2094,7 +2155,7 @@ class _GiftPageState extends State with TickerProviderStateMixin {
number: quantity,
customAnimationCount: animationCount,
type: SCRoomMsgType.luckGiftAnimOther,
- msg: jsonEncode(targetUserIds),
+ msg: jsonEncode(acceptUsers.map((u) => u.user?.id).toList()),
),
addLocal: true,
);
@@ -2117,26 +2178,6 @@ class _GiftPageState extends State with TickerProviderStateMixin {
return targetUserIds;
}
- List _resolveAcceptUserIdentityKeys(List acceptUsers) {
- final targetUserIds = [];
- void addTargetUserId(String? userId) {
- final normalizedUserId = (userId ?? "").trim();
- if (normalizedUserId.isEmpty ||
- targetUserIds.contains(normalizedUserId)) {
- return;
- }
- targetUserIds.add(normalizedUserId);
- }
-
- for (final acceptUser in acceptUsers) {
- final user = acceptUser.user;
- addTargetUserId(user?.id);
- addTargetUserId(user?.account);
- addTargetUserId(user?.getID());
- }
- return targetUserIds;
- }
-
String _createGiftBatchId(_GiftSendRequest request) {
final sortedAcceptUserIds = List.from(request.acceptUserIds)
..sort();
diff --git a/lib/modules/index/index_page.dart b/lib/modules/index/index_page.dart
index 312569a..1a29c29 100644
--- a/lib/modules/index/index_page.dart
+++ b/lib/modules/index/index_page.dart
@@ -441,12 +441,12 @@ class _SCIndexPageState extends State
return;
}
- await _showEntryPopupIfNeeded();
+ await _showDailySignInDialogIfNeeded();
if (!mounted) {
return;
}
- await _showDailySignInDialogIfNeeded();
+ await _showEntryPopupIfNeeded();
if (!mounted) {
return;
}
diff --git a/lib/modules/room/online/room_online_page.dart b/lib/modules/room/online/room_online_page.dart
index 0c989b6..af9b51d 100644
--- a/lib/modules/room/online/room_online_page.dart
+++ b/lib/modules/room/online/room_online_page.dart
@@ -13,7 +13,6 @@ import '../../../ui_kit/components/sc_page_list.dart';
import '../../../ui_kit/components/sc_tts.dart';
import '../../../ui_kit/components/text/sc_text.dart';
import '../../../ui_kit/widgets/id/sc_special_id_badge.dart';
-import '../../../shared/tools/sc_lk_dialog_util.dart';
import '../../../ui_kit/widgets/badge/sc_user_badge_strip.dart';
import '../../../ui_kit/widgets/room/room_user_info_card.dart';
@@ -44,7 +43,7 @@ class _RoomOnlinePageState
if (dialogContext == null) {
return;
}
- showBottomInBottomDialog(dialogContext, RoomUserInfoCard(userId: userId));
+ RoomUserInfoCard.showPreloaded(dialogContext, userId: userId);
});
}
diff --git a/lib/modules/room/rank/room_gift_rank_tab_page.dart b/lib/modules/room/rank/room_gift_rank_tab_page.dart
index 36e9f2a..49a644c 100644
--- a/lib/modules/room/rank/room_gift_rank_tab_page.dart
+++ b/lib/modules/room/rank/room_gift_rank_tab_page.dart
@@ -1,11 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
-import 'package:flutter_screenutil/flutter_screenutil.dart';
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
-import 'package:yumi/shared/business_logic/models/res/login_res.dart';
-import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
-import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:yumi/shared/business_logic/models/res/login_res.dart';
+import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
+import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
import 'package:yumi/main.dart';
import 'package:yumi/app_localizations.dart';
@@ -13,7 +13,6 @@ import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_page_list.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
-import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
import 'package:yumi/shared/business_logic/models/res/room_gift_rank_res.dart';
import 'package:yumi/ui_kit/widgets/id/sc_special_id_badge.dart';
import 'package:yumi/ui_kit/widgets/room/room_user_info_card.dart';
@@ -98,11 +97,11 @@ class _RoomGiftRankTabPageState
// headdress: userInfo.userProfile?.getHeaddress()?.sourceUrl,
),
onTap: () {
- showBottomInBottomDialog(
- navigatorKey.currentState!.context,
- RoomUserInfoCard(userId: userProfile?.id),
- );
SmartDialog.dismiss(tag: "showRoomGiftRankPage");
+ RoomUserInfoCard.showPreloaded(
+ navigatorKey.currentState!.context,
+ userId: userProfile?.id,
+ );
},
),
SizedBox(width: 3.w),
@@ -119,7 +118,7 @@ class _RoomGiftRankTabPageState
needScroll:
(userProfile?.userNickname?.characters.length ??
0) >
- 14,
+ 14,
),
],
),
@@ -137,7 +136,7 @@ class _RoomGiftRankTabPageState
animationHeight: 24.w,
showTextBesideAnimated: true,
animatedTextSpacing: 0,
- showAnimatedGradientText:
+ showAnimatedGradientText:
userProfile
?.shouldShowColoredSpecialIdText() ??
false,
@@ -195,24 +194,24 @@ class _RoomGiftRankTabPageState
SocialChatUserProfile? _mergeCurrentUserVip(
SocialChatUserProfile? profile,
) {
- if (profile == null || profile.vipLevelForColoredId > 0) {
- return profile;
- }
- final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
- if (currentProfile == null ||
- currentProfile.vipLevelForColoredId <= 0 ||
- currentProfile.id != profile.id) {
- return profile;
- }
- return profile.copyWith(
- vipLevel:
- currentProfile.vipLevel ??
- currentProfile.vipLevelForColoredId.toString(),
- );
- }
-
- @override
- empty() {
+ if (profile == null || profile.vipLevelForColoredId > 0) {
+ return profile;
+ }
+ final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
+ if (currentProfile == null ||
+ currentProfile.vipLevelForColoredId <= 0 ||
+ currentProfile.id != profile.id) {
+ return profile;
+ }
+ return profile.copyWith(
+ vipLevel:
+ currentProfile.vipLevel ??
+ currentProfile.vipLevelForColoredId.toString(),
+ );
+ }
+
+ @override
+ empty() {
return mainEmpty(
image: Image.asset(
'sc_images/general/sc_icon_loading.png',
diff --git a/lib/modules/room/seat/sc_seat_item.dart b/lib/modules/room/seat/sc_seat_item.dart
index 7653e4e..d517ab6 100644
--- a/lib/modules/room/seat/sc_seat_item.dart
+++ b/lib/modules/room/seat/sc_seat_item.dart
@@ -619,12 +619,15 @@ class _EmoticonsState extends State with TickerProviderStateMixin {
if (path.isEmpty) {
return SizedBox(width: width, height: width);
}
- return RoomEmojiAssetImage(
- key: ValueKey(path),
- asset: path,
- width: width,
- height: width,
- );
+ if (path.startsWith("assets/") || path.startsWith("sc_images/")) {
+ return RoomEmojiAssetImage(
+ key: ValueKey(path),
+ asset: path,
+ width: width,
+ height: width,
+ );
+ }
+ return netImage(url: path, width: width);
}
void _checkStart() {
diff --git a/lib/modules/room/voice_room_page.dart b/lib/modules/room/voice_room_page.dart
index 20dfa9d..7c6387b 100644
--- a/lib/modules/room/voice_room_page.dart
+++ b/lib/modules/room/voice_room_page.dart
@@ -175,7 +175,6 @@ class _VoiceRoomPageState extends State
void _handleRoomRouteVisible() {
_releaseCoveredRouteGuard();
_setRoomRouteVisible(true);
- _scheduleEnsureRoomVisualEffectsEnabled();
}
void _scheduleRoomRouteVisible() {
@@ -250,20 +249,13 @@ class _VoiceRoomPageState extends State
rtmProvider.msgFloatingGiftListener = _floatingGiftListener;
rtmProvider.msgLuckyGiftRewardTickerListener =
_luckyGiftRewardTickerListener;
- debugPrint('[GiftFx][voice] bind room visual effect listeners');
}
void _ensureRoomVisualEffectsEnabled() {
final rtcProvider = Provider.of(context, listen: false);
- if (rtcProvider.currenRoom == null || !_roomRouteVisible) {
- return;
- }
- final rtmProvider = Provider.of(context, listen: false);
- final needsListenerRebind =
- rtmProvider.msgFloatingGiftListener != _floatingGiftListener ||
- rtmProvider.msgLuckyGiftRewardTickerListener !=
- _luckyGiftRewardTickerListener;
- if (rtcProvider.roomVisualEffectsEnabled && !needsListenerRebind) {
+ if (rtcProvider.currenRoom == null ||
+ !_roomRouteVisible ||
+ rtcProvider.roomVisualEffectsEnabled) {
return;
}
_enableRoomVisualEffects();
@@ -694,44 +686,18 @@ class _VoiceRoomPageState extends State
_floatingGiftListener(Msg msg) {
final rtcProvider = Provider.of(context, listen: false);
if (!rtcProvider.shouldShowRoomVisualEffects) {
- debugPrint(
- '[GiftFx][voice] skip floating listener visual effects hidden '
- 'type=${msg.type} '
- 'giftId=${msg.gift?.id} '
- 'enabled=${rtcProvider.roomVisualEffectsEnabled} '
- 'visible=${rtcProvider.isVoiceRoomRouteVisible} '
- 'hasRoom=${rtcProvider.currenRoom != null}',
- );
return;
}
if (Provider.of(context, listen: false).hideLGiftAnimal) {
- debugPrint(
- '[GiftFx][voice] skip floating listener hidden by gift preference '
- 'type=${msg.type} giftId=${msg.gift?.id}',
- );
return;
}
final targetUserIds = _resolveGiftTargetUserIds(msg);
final targetGroupKey = _buildGiftTargetGroupKey(targetUserIds);
- debugPrint(
- '[GiftFx][voice] floating listener recv '
- 'type=${msg.type} '
- 'giftId=${msg.gift?.id} '
- 'giftTab=${msg.gift?.giftTab} '
- 'quantity=${msg.number} '
- 'animationCount=${msg.customAnimationCount} '
- 'giftPhoto=${msg.gift?.giftPhoto} '
- 'targetUserIds=${targetUserIds.join(",")} '
- 'targetGroupKey=$targetGroupKey',
- );
final shouldPlayBatchVisuals =
targetUserIds.length <= 1 ||
targetGroupKey == null ||
_markGiftVisualBatchForPlayback(msg, targetGroupKey);
if (shouldPlayBatchVisuals) {
- if (!mounted) {
- return;
- }
var giftModel = LGiftModel();
giftModel.labelId = _buildGiftTickerLabelId(msg, targetGroupKey);
giftModel.sendUserName = msg.user?.userNickname ?? "";
@@ -762,10 +728,7 @@ class _VoiceRoomPageState extends State
listen: false,
).enqueueGiftAnimation(giftModel);
} else {
- debugPrint(
- '[GiftFx][voice] skip gift ticker by visual batch dedupe '
- 'type=${msg.type} giftId=${msg.gift?.id} targetGroupKey=$targetGroupKey',
- );
+ return;
}
final giftPhoto = (msg.gift?.giftPhoto ?? "").trim();
@@ -781,7 +744,14 @@ class _VoiceRoomPageState extends State
targetGroupKey,
targetUserIds,
);
+ return;
}
+ _handleStandardGiftComboVisuals(
+ msg,
+ giftPhoto,
+ targetGroupKey,
+ targetUserIds,
+ );
}
void _enqueueCpGiftFloatingIfNeeded(Msg msg) {
@@ -893,9 +863,7 @@ class _VoiceRoomPageState extends State
bool _isLuckyGiftMessage(Msg msg) {
final giftTab = (msg.gift?.giftTab ?? '').trim();
- return giftTab == "LUCK" ||
- giftTab == SCGiftType.LUCKY_GIFT.name ||
- giftTab == SCGiftType.MAGIC.name;
+ return giftTab == "LUCK" || giftTab == SCGiftType.LUCKY_GIFT.name;
}
bool _supportsComboMilestoneEffects(Msg msg) {
@@ -944,24 +912,24 @@ class _VoiceRoomPageState extends State
}
}
- void _handleLuckyGiftComboVisuals(
+ void _handleStandardGiftComboVisuals(
Msg msg,
String giftPhoto,
String? targetGroupKey,
List targetUserIds,
) {
if ((msg.number ?? 0) <= 0) {
- debugPrint(
- '[GiftFx][voice] skip lucky seat flight invalid quantity '
- 'giftId=${msg.gift?.id} quantity=${msg.number}',
- );
return;
}
- if (targetGroupKey == null || targetUserIds.isEmpty) {
- debugPrint(
- '[GiftFx][voice] skip lucky seat flight empty targets '
- 'giftId=${msg.gift?.id} targetGroupKey=$targetGroupKey',
- );
+ if (!_shouldPlaySeatFlightGiftAnimation(msg) ||
+ targetGroupKey == null ||
+ targetUserIds.isEmpty ||
+ giftPhoto.isEmpty) {
+ return;
+ }
+ final flightBatchKey = _buildGiftFlightBatchKey(msg, targetUserIds);
+ if (targetUserIds.length > 1 &&
+ !_markGiftFlightBatchForPlayback(flightBatchKey)) {
return;
}
final sessionKey = _buildLuckyGiftComboSessionKey(msg, targetGroupKey);
@@ -972,25 +940,44 @@ class _VoiceRoomPageState extends State
session.endTimer?.cancel();
session.clearQueueTimer?.cancel();
- if (_shouldPlayLuckyGiftSeatFlightAnimation(msg) && giftPhoto.isNotEmpty) {
+ _enqueueTrackedSeatFlightAnimations(
+ sessionKey: sessionKey,
+ giftPhoto: giftPhoto,
+ targetUserIds: targetUserIds,
+ animationCount: _resolveTrackedAnimationCount(msg),
+ batchKey: flightBatchKey,
+ );
+
+ _scheduleGiftAnimationSessionEnd(sessionKey);
+ }
+
+ void _handleLuckyGiftComboVisuals(
+ Msg msg,
+ String giftPhoto,
+ String? targetGroupKey,
+ List targetUserIds,
+ ) {
+ if ((msg.number ?? 0) <= 0) {
+ return;
+ }
+ if (targetGroupKey == null || targetUserIds.isEmpty) {
+ return;
+ }
+ final sessionKey = _buildLuckyGiftComboSessionKey(msg, targetGroupKey);
+ final session = _luckyGiftComboSessions[sessionKey];
+ if (session == null) {
+ return;
+ }
+ session.endTimer?.cancel();
+ session.clearQueueTimer?.cancel();
+
+ if (_shouldPlaySeatFlightGiftAnimation(msg) && giftPhoto.isNotEmpty) {
final flightBatchKey = _buildGiftFlightBatchKey(msg, targetUserIds);
if (targetUserIds.length > 1 &&
!_markGiftFlightBatchForPlayback(flightBatchKey)) {
- debugPrint(
- '[GiftFx][voice] skip lucky seat flight by flight batch dedupe '
- 'giftId=${msg.gift?.id} batchKey=$flightBatchKey',
- );
_scheduleGiftAnimationSessionEnd(sessionKey);
return;
}
- debugPrint(
- '[GiftFx][voice] enqueue lucky seat flight '
- 'giftId=${msg.gift?.id} '
- 'giftPhoto=$giftPhoto '
- 'targetUserIds=${targetUserIds.join(",")} '
- 'animationCount=${_resolveTrackedAnimationCount(msg)} '
- 'batchKey=$flightBatchKey',
- );
_enqueueTrackedSeatFlightAnimations(
sessionKey: sessionKey,
giftPhoto: giftPhoto,
@@ -998,29 +985,10 @@ class _VoiceRoomPageState extends State
animationCount: _resolveTrackedAnimationCount(msg),
batchKey: flightBatchKey,
);
- } else {
- debugPrint(
- '[GiftFx][voice] skip lucky seat flight unsupported gift photo '
- 'giftId=${msg.gift?.id} giftPhoto=$giftPhoto',
- );
}
_scheduleGiftAnimationSessionEnd(sessionKey);
}
- bool _shouldPlayLuckyGiftSeatFlightAnimation(Msg msg) {
- final gift = msg.gift;
- if (gift == null) {
- return false;
- }
-
- final giftPhoto = (gift.giftPhoto ?? "").trim();
- if (giftPhoto.isEmpty) {
- return false;
- }
- final giftPhotoExt = _normalizedGiftResourceExtension(giftPhoto);
- return !_isAnimatedGiftResource(giftPhotoExt);
- }
-
int _resolveTrackedAnimationCount(Msg msg) {
return math.max(msg.customAnimationCount ?? 1, 1);
}
@@ -1050,51 +1018,21 @@ class _VoiceRoomPageState extends State
);
final normalizedTargetUserIds = _normalizeGiftTargetUserIds(targetUserIds);
if (normalizedTargetUserIds.isEmpty) {
- debugPrint(
- '[GiftFx][voice] skip seat flight enqueue empty normalized targets '
- 'sessionKey=$sessionKey',
- );
return;
}
- final rtcProvider = Provider.of(context, listen: false);
- final seenSeatKeys = >>{};
- final resolvedTargetUserIds = [];
- final unresolvedTargetUserIds = [];
- for (final targetUserId in normalizedTargetUserIds) {
- final targetKey = rtcProvider.getSeatGlobalKeyByIndex(targetUserId);
- if (targetKey == null) {
- unresolvedTargetUserIds.add(targetUserId);
- continue;
- }
- if (seenSeatKeys.add(targetKey)) {
- resolvedTargetUserIds.add(targetUserId);
- }
- }
- final flightTargetUserIds =
- resolvedTargetUserIds.isNotEmpty
- ? resolvedTargetUserIds
- : normalizedTargetUserIds;
- debugPrint(
- '[GiftFx][voice] seat flight targets '
- 'sessionKey=$sessionKey '
- 'requested=${normalizedTargetUserIds.join(",")} '
- 'resolved=${resolvedTargetUserIds.join(",")} '
- 'unresolved=${unresolvedTargetUserIds.join(",")} '
- 'used=${flightTargetUserIds.join(",")}',
- );
final normalizedAnimationCount = math.max(animationCount, 1);
final cappedAnimationCount = math.min(
normalizedAnimationCount,
_maxTrackedGiftAnimations,
);
final maxTrackedRequests = math.max(
- _maxTrackedGiftAnimations * flightTargetUserIds.length,
+ _maxTrackedGiftAnimations * normalizedTargetUserIds.length,
_maxTrackedGiftAnimations,
);
for (var index = 0; index < cappedAnimationCount; index += 1) {
final roundBatchTag =
- flightTargetUserIds.length > 1 ? '$batchKey|round:$index' : null;
- for (final targetUserId in flightTargetUserIds) {
+ normalizedTargetUserIds.length > 1 ? '$batchKey|round:$index' : null;
+ for (final targetUserId in normalizedTargetUserIds) {
_giftSeatFlightController.enqueueLimited(
RoomGiftSeatFlightRequest(
imagePath: giftPhoto,
@@ -1316,6 +1254,26 @@ class _VoiceRoomPageState extends State
_giftVisualBatchDedupeTimers.clear();
}
+ bool _shouldPlaySeatFlightGiftAnimation(Msg msg) {
+ final gift = msg.gift;
+ if (gift == null) {
+ return false;
+ }
+
+ final giftPhoto = (gift.giftPhoto ?? "").trim();
+ if (giftPhoto.isEmpty) {
+ return false;
+ }
+ final giftPhotoExt = _normalizedGiftResourceExtension(giftPhoto);
+ if (_isAnimatedGiftResource(giftPhotoExt)) {
+ return false;
+ }
+
+ final giftSourceUrl = (gift.giftSourceUrl ?? "").trim();
+ final sourceExt = _normalizedGiftResourceExtension(giftSourceUrl);
+ return !_isAnimatedGiftResource(sourceExt);
+ }
+
bool _isAnimatedGiftResource(String extension) {
return extension == ".svga" || extension == ".mp4" || extension == ".vap";
}
diff --git a/lib/modules/room_game/bridge/baishun_js_bridge.dart b/lib/modules/room_game/bridge/baishun_js_bridge.dart
index 7eeb385..ad15a0f 100644
--- a/lib/modules/room_game/bridge/baishun_js_bridge.dart
+++ b/lib/modules/room_game/bridge/baishun_js_bridge.dart
@@ -7,6 +7,7 @@ class BaishunBridgeActions {
static const String gameLoaded = 'gameLoaded';
static const String gameRecharge = 'gameRecharge';
static const String destroy = 'destroy';
+ static const String debugLog = 'debugLog';
}
class BaishunBridgeMessage {
@@ -103,6 +104,45 @@ class BaishunJsBridge {
$channelName.postMessage(JSON.stringify({ action: action, payload: payload }));
return payload;
}
+ function clipText(value, limit) {
+ const text = (value == null ? '' : String(value)).trim();
+ if (!text) {
+ return '';
+ }
+ return text.length > limit ? text.slice(0, limit) + '...' : text;
+ }
+ function stringifyForLog(value) {
+ if (value == null) {
+ return '';
+ }
+ if (typeof value === 'string') {
+ return clipText(value, 320);
+ }
+ try {
+ return clipText(JSON.stringify(value), 320);
+ } catch (_) {
+ return clipText(String(value), 320);
+ }
+ }
+ function postDebug(tag, payload) {
+ $channelName.postMessage(JSON.stringify({
+ action: '${BaishunBridgeActions.debugLog}',
+ payload: {
+ tag: tag,
+ message: stringifyForLog(payload)
+ }
+ }));
+ }
+ function maskText(value) {
+ const text = (value == null ? '' : String(value)).trim();
+ if (!text) {
+ return '';
+ }
+ if (text.length <= 10) {
+ return text;
+ }
+ return text.slice(0, 6) + '***' + text.slice(-4);
+ }
function ensureChannelAlias(name, handler) {
if (!window[name] || typeof window[name].postMessage !== 'function') {
window[name] = { postMessage: handler };
@@ -116,21 +156,30 @@ class BaishunJsBridge {
}
}
window.NativeBridge.getConfigSync = function() {
+ postDebug('bridge.getConfigSync', {
+ hasConfig: !!window.__baishunLastConfig,
+ hasNativeBridgeConfig: !!(window.NativeBridge && window.NativeBridge.config)
+ });
return window.__baishunLastConfig || null;
};
window.NativeBridge.getConfig = function(params) {
+ postDebug('bridge.getConfig.call', toPayload(params));
postAction('${BaishunBridgeActions.getConfig}', params);
return window.__baishunLastConfig || null;
};
window.NativeBridge.destroy = function(payload) {
+ postDebug('bridge.destroy.call', toPayload(payload));
postAction('${BaishunBridgeActions.destroy}', payload);
};
window.NativeBridge.gameRecharge = function(payload) {
+ postDebug('bridge.gameRecharge.call', toPayload(payload));
postAction('${BaishunBridgeActions.gameRecharge}', payload);
};
window.NativeBridge.gameLoaded = function(payload) {
+ postDebug('bridge.gameLoaded.call', toPayload(payload));
postAction('${BaishunBridgeActions.gameLoaded}', payload);
};
+ window.__baishunDebugLog = postDebug;
ensureChannelAlias('getConfig', function(message) {
window.NativeBridge.getConfig(message);
});
@@ -155,6 +204,163 @@ class BaishunJsBridge {
ensureWebkitHandler('gameLoaded', function(message) {
window.NativeBridge.gameLoaded(message);
});
+ if (!window.__baishunNetworkDebugReady) {
+ window.__baishunNetworkDebugReady = true;
+ if (typeof document !== 'undefined' && typeof document.addEventListener === 'function' && !window.__baishunInputDebugReady) {
+ window.__baishunInputDebugReady = true;
+ window.__baishunInputDebugCount = 0;
+ ['pointerdown', 'touchstart', 'mousedown', 'click'].forEach(function(eventName) {
+ document.addEventListener(eventName, function(event) {
+ if ((window.__baishunInputDebugCount || 0) >= 24) {
+ return;
+ }
+ window.__baishunInputDebugCount = (window.__baishunInputDebugCount || 0) + 1;
+ const target = event && event.target;
+ const touch = event && event.touches && event.touches.length ? event.touches[0] : null;
+ postDebug('input.' + eventName, {
+ x: touch ? touch.clientX : (event && event.clientX),
+ y: touch ? touch.clientY : (event && event.clientY),
+ pointerType: event && event.pointerType,
+ targetTag: target && target.tagName,
+ targetId: target && target.id,
+ targetClass: target && target.className
+ });
+ }, true);
+ });
+ }
+ if (typeof window.addEventListener === 'function') {
+ window.addEventListener('error', function(event) {
+ const target = event && event.target;
+ if (target && target !== window) {
+ postDebug('resource.error', {
+ tag: target.tagName || '',
+ source: target.currentSrc || target.src || target.href || '',
+ outerHTML: target.outerHTML || ''
+ });
+ return;
+ }
+ postDebug('window.error', {
+ message: event && event.message,
+ source: event && event.filename,
+ line: event && event.lineno,
+ column: event && event.colno,
+ stack: event && event.error && event.error.stack
+ });
+ }, true);
+ window.addEventListener('unhandledrejection', function(event) {
+ postDebug('promise.reject', {
+ reason: event && event.reason
+ });
+ });
+ }
+ if (window.console && !window.__baishunConsoleDebugReady) {
+ window.__baishunConsoleDebugReady = true;
+ ['log', 'info', 'warn', 'error'].forEach(function(level) {
+ const original = typeof window.console[level] === 'function'
+ ? window.console[level].bind(window.console)
+ : null;
+ window.console[level] = function() {
+ try {
+ postDebug('console.' + level, Array.prototype.slice.call(arguments));
+ } catch (_) {}
+ if (original) {
+ return original.apply(window.console, arguments);
+ }
+ return undefined;
+ };
+ });
+ }
+ if (typeof window.fetch === 'function') {
+ const originalFetch = window.fetch.bind(window);
+ window.fetch = function(input, init) {
+ const url = typeof input === 'string' ? input : (input && input.url) || '';
+ const method = (init && init.method) || 'GET';
+ postDebug('fetch.start', { method: method, url: url });
+ return originalFetch(input, init)
+ .then(function(response) {
+ const cloned = response && typeof response.clone === 'function' ? response.clone() : null;
+ if (cloned && typeof cloned.text === 'function') {
+ cloned.text().then(function(text) {
+ if (!response.ok || /failed|error|exception|get user info/i.test(text)) {
+ postDebug('fetch.end', {
+ method: method,
+ url: url,
+ status: response.status,
+ body: text
+ });
+ } else {
+ postDebug('fetch.end', {
+ method: method,
+ url: url,
+ status: response.status
+ });
+ }
+ }).catch(function(error) {
+ postDebug('fetch.readError', {
+ method: method,
+ url: url,
+ error: error && error.message ? error.message : String(error)
+ });
+ });
+ } else {
+ postDebug('fetch.end', {
+ method: method,
+ url: url,
+ status: response && response.status
+ });
+ }
+ return response;
+ })
+ .catch(function(error) {
+ postDebug('fetch.error', {
+ method: method,
+ url: url,
+ error: error && error.message ? error.message : String(error)
+ });
+ throw error;
+ });
+ };
+ }
+ if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) {
+ const xhrOpen = window.XMLHttpRequest.prototype.open;
+ const xhrSend = window.XMLHttpRequest.prototype.send;
+ window.XMLHttpRequest.prototype.open = function(method, url) {
+ this.__baishunMethod = method || 'GET';
+ this.__baishunUrl = url || '';
+ return xhrOpen.apply(this, arguments);
+ };
+ window.XMLHttpRequest.prototype.send = function(body) {
+ const method = this.__baishunMethod || 'GET';
+ const url = this.__baishunUrl || '';
+ postDebug('xhr.start', { method: method, url: url });
+ this.addEventListener('loadend', function() {
+ const responseText = typeof this.responseText === 'string' ? this.responseText : '';
+ if (this.status >= 400 || /failed|error|exception|get user info/i.test(responseText)) {
+ postDebug('xhr.end', {
+ method: method,
+ url: url,
+ status: this.status,
+ body: responseText
+ });
+ } else {
+ postDebug('xhr.end', {
+ method: method,
+ url: url,
+ status: this.status
+ });
+ }
+ });
+ this.addEventListener('error', function() {
+ postDebug('xhr.error', {
+ method: method,
+ url: url,
+ status: this.status
+ });
+ });
+ return xhrSend.apply(this, arguments);
+ };
+ }
+ }
window.baishunChannel = window.baishunChannel || {};
window.baishunChannel.walletUpdate = function(payload) {
window.__baishunLastWalletPayload = payload || {};
@@ -171,6 +377,10 @@ class BaishunJsBridge {
if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
window.dispatchEvent(new CustomEvent('baishunBridgeReady'));
}
+ postDebug('bridge.ready', {
+ href: window.location && window.location.href,
+ ua: navigator && navigator.userAgent
+ });
})();
''';
}
@@ -185,6 +395,16 @@ class BaishunJsBridge {
(function() {
const config = $payload;
const explicitCallbackPath = $encodedCallbackPath;
+ function maskText(value) {
+ const text = (value == null ? '' : String(value)).trim();
+ if (!text) {
+ return '';
+ }
+ if (text.length <= 10) {
+ return text;
+ }
+ return text.slice(0, 6) + '***' + text.slice(-4);
+ }
function resolvePath(path) {
if (!path || typeof path !== 'string') {
return null;
@@ -224,6 +444,20 @@ class BaishunJsBridge {
if (explicitCallbackPath) {
window.__baishunLastJsCallback = explicitCallbackPath;
}
+ if (typeof window.__baishunDebugLog === 'function') {
+ window.__baishunDebugLog('config.deliver', {
+ appId: config.appId,
+ appChannel: config.appChannel,
+ userId: config.userId,
+ roomId: config.roomId,
+ gameMode: config.gameMode,
+ language: config.language,
+ gsp: config.gsp,
+ code: maskText(config.code),
+ codeLength: config.code ? String(config.code).length : 0,
+ callbackPath: callbackPath
+ });
+ }
if (typeof window.__baishunGetConfigCallback === 'function') {
window.__baishunGetConfigCallback(config);
window.__baishunGetConfigCallback = null;
@@ -231,6 +465,14 @@ class BaishunJsBridge {
if (window.__baishunLastJsCallback) {
callbackInvoked = invokeCallbackPath(window.__baishunLastJsCallback) || callbackInvoked;
}
+ if (typeof window.__baishunDebugLog === 'function') {
+ window.__baishunDebugLog('config.callback', {
+ callbackPath: callbackPath,
+ callbackInvoked: callbackInvoked,
+ hasLegacyCallback: typeof window.__baishunGetConfigCallback === 'function',
+ hasOnBaishunConfig: typeof window.onBaishunConfig === 'function'
+ });
+ }
if (typeof window.onBaishunConfig === 'function') {
window.onBaishunConfig(config);
}
@@ -252,6 +494,9 @@ class BaishunJsBridge {
return '''
(function() {
const payload = $safePayload;
+ if (typeof window.__baishunDebugLog === 'function') {
+ window.__baishunDebugLog('wallet.update', payload);
+ }
if (window.baishunChannel && typeof window.baishunChannel.walletUpdate === 'function') {
window.baishunChannel.walletUpdate(payload);
}
diff --git a/lib/modules/room_game/bridge/leader_js_bridge.dart b/lib/modules/room_game/bridge/leader_js_bridge.dart
index a40c69a..23effc1 100644
--- a/lib/modules/room_game/bridge/leader_js_bridge.dart
+++ b/lib/modules/room_game/bridge/leader_js_bridge.dart
@@ -5,6 +5,7 @@ class LeaderBridgeActions {
static const String pay = 'pay';
static const String closeGame = 'closeGame';
static const String loadComplete = 'loadComplete';
+ static const String debugLog = 'debugLog';
}
class LeaderBridgeMessage {
@@ -87,6 +88,21 @@ class LeaderJsBridge {
return {};
}
+ function stringifyForLog(value) {
+ if (value == null) {
+ return '';
+ }
+ if (typeof value === 'string') {
+ return value.length > 320 ? value.slice(0, 320) + '...' : value;
+ }
+ try {
+ const text = JSON.stringify(value);
+ return text.length > 320 ? text.slice(0, 320) + '...' : text;
+ } catch (_) {
+ return String(value);
+ }
+ }
+
function postMessage(action, input) {
const payload = toPayload(input);
$channelName.postMessage(JSON.stringify({
@@ -96,6 +112,13 @@ class LeaderJsBridge {
return payload;
}
+ function postDebug(tag, payload) {
+ postMessage('${LeaderBridgeActions.debugLog}', {
+ tag: tag,
+ message: stringifyForLog(payload)
+ });
+ }
+
function invokeCallback(callback, payload) {
if (typeof callback === 'function') {
callback(payload);
@@ -176,6 +199,16 @@ class LeaderJsBridge {
};
if (payloadChanged) {
dispatchConfigReady(normalizedPayload);
+ postDebug('config.ready', {
+ provider: normalizedPayload.provider || '',
+ gameId: normalizedPayload.gameId || '',
+ roomId: launchConfig.roomId || '',
+ code: launchConfig.code
+ ? String(launchConfig.code).slice(0, 6) + '***'
+ : '',
+ codeLength: launchConfig.code ? String(launchConfig.code).length : 0,
+ entryUrl: entry.entryUrl || ''
+ });
}
}
@@ -201,6 +234,12 @@ class LeaderJsBridge {
window.NativeBridge.pay = pay;
window.NativeBridge.closeGame = closeGame;
window.NativeBridge.loadComplete = loadComplete;
+
+ window.__leaderBridgeDebug = postDebug;
+ postDebug('bridge.ready', {
+ href: window.location && window.location.href,
+ hasUpdateCoin: typeof window.updateCoin === 'function'
+ });
}
applyLaunchPayload(launchPayload);
@@ -223,6 +262,13 @@ class LeaderJsBridge {
window.onUpdateCoin();
return;
}
+ $channelName.postMessage(JSON.stringify({
+ action: '${LeaderBridgeActions.debugLog}',
+ payload: {
+ tag: 'game.updateCoin.missing',
+ message: 'No updateCoin handler found on game page.'
+ }
+ }));
})();
''';
}
diff --git a/lib/modules/room_game/views/baishun_game_page.dart b/lib/modules/room_game/views/baishun_game_page.dart
index ba291ae..6c2a1bb 100644
--- a/lib/modules/room_game/views/baishun_game_page.dart
+++ b/lib/modules/room_game/views/baishun_game_page.dart
@@ -299,6 +299,12 @@ class _BaishunGamePageState extends State {
Future _dispatchBridgeMessage(
BaishunBridgeMessage bridgeMessage,
) async {
+ if (bridgeMessage.action == BaishunBridgeActions.debugLog) {
+ final tag = bridgeMessage.payload['tag']?.toString().trim();
+ final message = bridgeMessage.payload['message']?.toString() ?? '';
+ _log('h5_debug tag=${tag ?? 'unknown'} message=${_clip(message, 800)}');
+ return;
+ }
final callbackPath = _extractCallbackPath(bridgeMessage.payload);
_log(
'bridge_action action=${bridgeMessage.action} '
@@ -406,11 +412,7 @@ class _BaishunGamePageState extends State {
return '';
}
- void _log(String message) {
- if (kDebugMode) {
- debugPrint('$_logPrefix $message');
- }
- }
+ void _log(String message) {}
String _clip(String value, [int limit = 600]) {
final trimmed = value.trim();
@@ -680,12 +682,10 @@ class _BaishunGamePageState extends State {
),
if (_errorMessage != null) _buildErrorState(),
if (_isLoading && _errorMessage == null)
- const Positioned.fill(
- child: IgnorePointer(
- ignoring: true,
- child: BaishunLoadingView(
- message: 'Waiting for gameLoaded...',
- ),
+ const IgnorePointer(
+ ignoring: true,
+ child: BaishunLoadingView(
+ message: 'Waiting for gameLoaded...',
),
),
],
diff --git a/lib/modules/room_game/views/baishun_loading_view.dart b/lib/modules/room_game/views/baishun_loading_view.dart
index 2f14d61..cbf3984 100644
--- a/lib/modules/room_game/views/baishun_loading_view.dart
+++ b/lib/modules/room_game/views/baishun_loading_view.dart
@@ -14,7 +14,7 @@ class BaishunLoadingView extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return SizedBox.expand(
+ return Positioned.fill(
child: Container(
color: backgroundColor,
alignment: Alignment.center,
diff --git a/lib/modules/room_game/views/leader_game_page.dart b/lib/modules/room_game/views/leader_game_page.dart
index fa1f372..485aea6 100644
--- a/lib/modules/room_game/views/leader_game_page.dart
+++ b/lib/modules/room_game/views/leader_game_page.dart
@@ -245,6 +245,13 @@ class _LeaderGamePageState extends State {
}
Future _dispatchBridgeMessage(LeaderBridgeMessage bridgeMessage) async {
+ if (bridgeMessage.action == LeaderBridgeActions.debugLog) {
+ final tag = bridgeMessage.payload['tag']?.toString().trim();
+ final message = bridgeMessage.payload['message']?.toString() ?? '';
+ _log('h5_debug tag=${tag ?? 'unknown'} message=${_clip(message, 800)}');
+ return;
+ }
+
_didReceiveBridgeMessage = true;
_stopBridgeBootstrap(reason: 'bridge_message_${bridgeMessage.action}');
_log(
@@ -378,11 +385,7 @@ class _LeaderGamePageState extends State {
return fallback;
}
- void _log(String message) {
- if (kDebugMode) {
- debugPrint('$_logPrefix $message');
- }
- }
+ void _log(String message) {}
String _maskValue(String value, {int keepStart = 6, int keepEnd = 4}) {
final trimmed = value.trim();
@@ -585,12 +588,10 @@ class _LeaderGamePageState extends State {
),
if (_errorMessage != null) _buildErrorState(),
if (_isLoading && _errorMessage == null)
- const Positioned.fill(
- child: IgnorePointer(
- ignoring: true,
- child: BaishunLoadingView(
- message: 'Waiting for Leader game...',
- ),
+ const IgnorePointer(
+ ignoring: true,
+ child: BaishunLoadingView(
+ message: 'Waiting for Leader game...',
),
),
],
diff --git a/lib/modules/room_game/views/room_game_list_sheet.dart b/lib/modules/room_game/views/room_game_list_sheet.dart
index cda0788..076d34b 100644
--- a/lib/modules/room_game/views/room_game_list_sheet.dart
+++ b/lib/modules/room_game/views/room_game_list_sheet.dart
@@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:math' as math;
-import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
@@ -35,7 +34,6 @@ class RoomGameListSheet extends StatefulWidget {
class _RoomGameListSheetState extends State {
static const int _itemsPerRow = 4;
- static const String _logPrefix = '[RoomGameList]';
static const String _sheetFrameAsset =
'sc_images/room/sc_room_game_sheet_frame.png';
static const String _dividerLayerAsset =
@@ -196,11 +194,7 @@ class _RoomGameListSheetState extends State {
}
}
- void _log(String message) {
- if (kDebugMode) {
- debugPrint('$_logPrefix $message');
- }
- }
+ void _log(String message) {}
String _clip(String value, [int limit = 600]) {
final trimmed = value.trim();
diff --git a/lib/modules/settings/language/language_page.dart b/lib/modules/settings/language/language_page.dart
index f81b4d5..66c7ed7 100644
--- a/lib/modules/settings/language/language_page.dart
+++ b/lib/modules/settings/language/language_page.dart
@@ -32,8 +32,6 @@ class _LanguagePageState extends State {
_LanguageOption(code: 'it', label: 'Italiano'),
_LanguageOption(code: 'pt', label: 'Português'),
_LanguageOption(code: 'fr', label: 'Français'),
- _LanguageOption(code: 'id', label: 'Bahasa Indonesia'),
- _LanguageOption(code: 'es', label: 'Español'),
];
String _selectedLanguageCode = 'en';
diff --git a/lib/modules/user/me_page2.dart b/lib/modules/user/me_page2.dart
index a9b42e2..795d265 100644
--- a/lib/modules/user/me_page2.dart
+++ b/lib/modules/user/me_page2.dart
@@ -37,6 +37,8 @@ class MePage2 extends StatefulWidget {
class _MePage2State extends State with WidgetsBindingObserver {
static const Duration _counterStaleDuration = Duration(seconds: 30);
+ static const String _cpActivityUrl =
+ 'https://h5.global-interaction.com/activity/cp-space/yumi.html';
static const String _inviteActivityUrl =
'https://h5.haiyihy.com/app-invite/index.html';
static const double _inviteActivityHorizontalScale = 750 / 662;
@@ -299,6 +301,21 @@ class _MePage2State extends State with WidgetsBindingObserver {
);
}
+ void _openCpActivity() {
+ final cpActivityUrl = _cpActivityUrl.trim();
+ if (cpActivityUrl.isEmpty) {
+ if (kDebugMode) {
+ debugPrint('[MePage2] cp activity url is empty');
+ }
+ return;
+ }
+
+ SCNavigatorUtils.push(
+ context,
+ "${SCMainRoute.webViewPage}?url=${Uri.encodeComponent(cpActivityUrl)}&showTitle=false",
+ );
+ }
+
String _appendToken(String url) {
final token = AccountStorage().getToken();
if (token.isEmpty) return url;
@@ -480,6 +497,14 @@ class _MePage2State extends State with WidgetsBindingObserver {
iconPath: 'sc_images/index/sc_icon_bag.png',
onTap: () => SCNavigatorUtils.push(context, StoreRoute.bags),
),
+ SizedBox(width: 10.w),
+ _buildEntryItem(
+ title: 'cp',
+ iconPath: 'sc_images/index/sc_icon_cp_entry.png',
+ iconWidth: 47,
+ iconHeight: 50,
+ onTap: _openCpActivity,
+ ),
],
);
}
diff --git a/lib/modules/user/profile/profile_gift_wall_detail_page.dart b/lib/modules/user/profile/profile_gift_wall_detail_page.dart
index 9395813..d24dcfb 100644
--- a/lib/modules/user/profile/profile_gift_wall_detail_page.dart
+++ b/lib/modules/user/profile/profile_gift_wall_detail_page.dart
@@ -568,7 +568,6 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
late final SVGAAnimationController _controller;
bool _loaded = false;
bool _finished = false;
- String? _retainedResource;
@override
void initState() {
@@ -587,10 +586,7 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
Future _loadAndPlay() async {
try {
final movieEntity = await _loadMovieEntity(widget.sourceUrl);
- if (!mounted || _finished) {
- _releaseRetainedResource();
- return;
- }
+ if (!mounted || _finished) return;
setState(() {
_loaded = true;
_controller.videoItem = movieEntity;
@@ -604,12 +600,9 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
}
Future _loadMovieEntity(String sourceUrl) async {
- final manager = SCGiftVapSvgaManager();
- final cached = manager.getCachedSvgaEntity(sourceUrl, retain: true);
- if (cached != null) {
- _replaceRetainedResource(sourceUrl);
- return cached;
- }
+ final cache = SCGiftVapSvgaManager().videoItemCache;
+ final cached = cache[sourceUrl];
+ if (cached != null) return cached;
final pathType = SCPathUtils.getPathType(sourceUrl);
late final MovieEntity movieEntity;
@@ -624,35 +617,10 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
throw Exception("Unsupported SVGA source: $sourceUrl");
}
movieEntity.autorelease = false;
- if (!mounted || _finished) {
- movieEntity.dispose();
- return movieEntity;
- }
- manager.cacheSvgaEntity(sourceUrl, movieEntity, retain: true);
- _replaceRetainedResource(sourceUrl);
+ cache[sourceUrl] = movieEntity;
return movieEntity;
}
- void _replaceRetainedResource(String resource) {
- final previous = _retainedResource;
- if (previous == resource) {
- return;
- }
- if (previous != null) {
- SCGiftVapSvgaManager().releaseCachedSvgaEntity(previous);
- }
- _retainedResource = resource;
- }
-
- void _releaseRetainedResource() {
- final resource = _retainedResource;
- if (resource == null) {
- return;
- }
- _retainedResource = null;
- SCGiftVapSvgaManager().releaseCachedSvgaEntity(resource);
- }
-
void _finish() {
if (_finished) return;
_finished = true;
@@ -661,7 +629,6 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
@override
void dispose() {
- _releaseRetainedResource();
_controller.removeStatusListener(_handleStatusChanged);
_controller.dispose();
super.dispose();
diff --git a/lib/modules/wallet/recharge/recharge_page.dart b/lib/modules/wallet/recharge/recharge_page.dart
index eaa3c30..1eb18a3 100644
--- a/lib/modules/wallet/recharge/recharge_page.dart
+++ b/lib/modules/wallet/recharge/recharge_page.dart
@@ -37,11 +37,6 @@ class _RechargePageState extends State {
context,
listen: false,
).initializePaymentProcessor(context);
- } else if (Platform.isIOS) {
- Provider.of(
- context,
- listen: false,
- ).initializePaymentProcessor(context);
}
}
@@ -60,7 +55,7 @@ class _RechargePageState extends State {
.getRechargePageScaffoldBackgroundColor(),
resizeToAvoidBottomInset: false,
appBar: SocialChatStandardAppBar(
- title: SCAppLocalizations.of(context)!.recharge,
+ title: SCAppLocalizations.of(context)!.wallet,
actions: [
GestureDetector(
onTap: _showRecordDialog,
@@ -83,38 +78,27 @@ class _RechargePageState extends State {
_buildWalletSection(),
SizedBox(height: 36.w),
Expanded(
- child: Padding(
- padding: EdgeInsets.fromLTRB(12.w, 8.w, 12.w, 0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- _buildMethodSection(),
- SizedBox(height: 14.w),
- Expanded(child: _buildNativeProductList()),
- if (Platform.isIOS) ...[
- SizedBox(height: 12.w),
- _buildFooterButton(
- title:
- SCAppLocalizations.of(
- context,
- )!.restorePurchases,
- onTap: () {
- Provider.of(
- context,
- listen: false,
- ).recoverTransactions();
- },
+ child:
+ Platform.isIOS
+ ? const SizedBox.shrink()
+ : Padding(
+ padding: EdgeInsets.fromLTRB(12.w, 8.w, 12.w, 0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ _buildMethodSection(),
+ SizedBox(height: 14.w),
+ Expanded(child: _buildNativeProductList()),
+ SizedBox(height: 12.w),
+ _buildFooterButton(
+ title:
+ SCAppLocalizations.of(context)!.recharge,
+ onTap: _processNativePurchase,
+ ),
+ SizedBox(height: 14.w),
+ ],
+ ),
),
- ],
- SizedBox(height: 12.w),
- _buildFooterButton(
- title: SCAppLocalizations.of(context)!.recharge,
- onTap: _processNativePurchase,
- ),
- SizedBox(height: 14.w),
- ],
- ),
- ),
),
],
),
diff --git a/lib/services/audio/rtc_manager.dart b/lib/services/audio/rtc_manager.dart
index a8450a6..f1fe40d 100644
--- a/lib/services/audio/rtc_manager.dart
+++ b/lib/services/audio/rtc_manager.dart
@@ -51,8 +51,6 @@ import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart';
import '../../shared/data_sources/models/enum/sc_room_info_event_type.dart';
import '../../shared/data_sources/models/enum/sc_room_roles_type.dart';
import '../../shared/business_logic/models/res/sc_is_follow_room_res.dart';
-import '../../shared/business_logic/models/res/sc_mic_go_up_res.dart'
- hide PropsResources;
import '../../shared/business_logic/models/res/sc_room_rocket_api_res.dart';
import '../../shared/business_logic/models/res/sc_room_red_packet_config_res.dart';
import '../../shared/business_logic/models/res/sc_room_red_packet_list_res.dart';
@@ -339,9 +337,11 @@ class _RecentMicChangeSeatSnapshot {
class RealTimeCommunicationManager extends ChangeNotifier {
static const String _roomRocketPagLaunchDialogTag =
'showRoomRocketPagLaunchDialog';
- static const Duration _roomRocketGiftRefreshThrottleDelay = Duration(
- milliseconds: 700,
- );
+ static const List _roomRocketGiftRefreshDelays = [
+ Duration(milliseconds: 700),
+ Duration(milliseconds: 1600),
+ Duration(milliseconds: 3200),
+ ];
static const List _roomRocketPostLaunchRefreshDelays = [
Duration(milliseconds: 800),
Duration(seconds: 2),
@@ -406,10 +406,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
Timer? _roomEntryEffectTimer;
Timer? _roomRocketLaunchAnimationTimer;
ValueNotifier? _roomRocketLaunchTopAvatarNotifier;
- Timer? _roomRocketGiftRefreshTimer;
- String? _roomRocketGiftRefreshRoomId;
- bool _roomRocketGiftRefreshDirty = false;
- bool _roomRocketGiftRefreshInFlight = false;
+ final List _roomRocketGiftRefreshTimers = [];
final List _roomRocketPostLaunchRefreshTimers = [];
final List _roomRocketEntryReadyFallbackTimers = [];
Timer? _roomRedPacketPresenceTimer;
@@ -438,7 +435,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
int? _pendingSelfMicSwitchGuardUntilMs;
num? _pendingSelfMicReleaseIndex;
int? _pendingSelfMicReleaseGuardUntilMs;
- final Set _confirmedSelfMicIdentityKeys = {};
int? _seatResizeOccupantPreserveUntilMs;
_RoomRtcLocalAudioRole? _lastAppliedLocalAudioRole;
bool? _lastAppliedLocalAudioMuted;
@@ -484,24 +480,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
///禁音开关 默认关闭
bool isMic = true;
- bool _setSelfMicMuted(bool value) {
- if (isMic == value) {
- return false;
- }
- isMic = value;
- return true;
- }
-
- void _notifySelfMicUiState({bool? muted, bool notifyIfUnchanged = false}) {
- var changed = false;
- if (muted != null) {
- changed = _setSelfMicMuted(muted) || changed;
- }
- if (changed || notifyIfUnchanged) {
- notifyListeners();
- }
- }
-
bool shouldShowSeatSoundWave(num index) {
final seat = roomWheatMap[index];
final userId = (seat?.user?.id ?? "").trim();
@@ -546,13 +524,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
Set _currentUserIdentityKeys() {
- final currentUser = AccountStorage().getCurrentUser()?.userProfile;
- return {
- currentUser?.id?.trim() ?? "",
- currentUser?.account?.trim() ?? "",
- currentUser?.getID().trim() ?? "",
- ..._confirmedSelfMicIdentityKeys,
- }..removeWhere((item) => item.isEmpty);
+ return _userProfileIdentityKeys(
+ AccountStorage().getCurrentUser()?.userProfile,
+ );
}
Set _userProfileIdentityKeys(SocialChatUserProfile? user) {
@@ -573,16 +547,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
return _userProfileIdentityKeys(user).any(identityKeys.contains);
}
- void _rememberConfirmedSelfMicIdentity(SocialChatUserProfile? user) {
- _confirmedSelfMicIdentityKeys
- ..clear()
- ..addAll(_userProfileIdentityKeys(user));
- }
-
- void _clearConfirmedSelfMicIdentity() {
- _confirmedSelfMicIdentityKeys.clear();
- }
-
///房间红包列表
List redPacketList = [];
@@ -1331,8 +1295,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
bool _shouldSuppressCurrentUserOnSeat(num index, {MicRes? seat}) {
- final currentUserKeys = _currentUserIdentityKeys();
- if (currentUserKeys.isEmpty) {
+ final currentUserId =
+ (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
+ if (currentUserId.isEmpty) {
return false;
}
final releaseIndex = _pendingSelfMicReleaseIndex;
@@ -1342,7 +1307,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
return false;
}
final resolvedSeat = seat ?? roomWheatMap[index];
- return _userProfileMatchesAnyIdentity(resolvedSeat?.user, currentUserKeys);
+ return (resolvedSeat?.user?.id ?? "").trim() == currentUserId;
}
MicRes? micAtIndexForDisplay(num index) {
@@ -1369,8 +1334,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
Map nextMap, {
required Map previousMap,
}) {
- final currentUserKeys = _currentUserIdentityKeys();
- if (currentUserKeys.isEmpty) {
+ final currentUserId =
+ (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
+ if (currentUserId.isEmpty) {
return nextMap;
}
final nowMs = DateTime.now().millisecondsSinceEpoch;
@@ -1382,20 +1348,12 @@ class RealTimeCommunicationManager extends ChangeNotifier {
targetIndex != null && sourceIndex != null && guardUntilMs > nowMs;
final selfSeatIndices =
nextMap.entries
- .where(
- (entry) => _userProfileMatchesAnyIdentity(
- entry.value.user,
- currentUserKeys,
- ),
- )
+ .where((entry) => entry.value.user?.id == currentUserId)
.map((entry) => entry.key)
.toList();
if (targetIndex != null &&
- _userProfileMatchesAnyIdentity(
- nextMap[targetIndex]?.user,
- currentUserKeys,
- )) {
+ nextMap[targetIndex]?.user?.id == currentUserId) {
_clearSelfMicSwitchGuard();
_clearSelfMicGoUpGuard();
return nextMap;
@@ -1413,7 +1371,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
previousMap: previousMap,
selfSeatIndices: selfSeatIndices,
targetIndex: goUpIndex,
- currentUserKeys: currentUserKeys,
+ currentUserId: currentUserId,
);
if (stabilizedMap != null) {
return stabilizedMap;
@@ -1439,7 +1397,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
previousMap: previousMap,
selfSeatIndices: selfSeatIndices,
targetIndex: resolvedTargetIndex,
- currentUserKeys: currentUserKeys,
+ currentUserId: currentUserId,
);
if (stabilizedMap != null) {
return stabilizedMap;
@@ -1466,7 +1424,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
final releaseSeat = nextMap[releaseIndex];
- if (!_userProfileMatchesAnyIdentity(releaseSeat?.user, currentUserKeys)) {
+ if (releaseSeat?.user?.id != currentUserId) {
_clearSelfMicReleaseGuard();
return nextMap;
}
@@ -1481,22 +1439,16 @@ class RealTimeCommunicationManager extends ChangeNotifier {
required Map previousMap,
required Iterable selfSeatIndices,
required num targetIndex,
- required Set currentUserKeys,
+ required String currentUserId,
}) {
final optimisticTargetSeat = previousMap[targetIndex];
if (optimisticTargetSeat == null ||
- !_userProfileMatchesAnyIdentity(
- optimisticTargetSeat.user,
- currentUserKeys,
- )) {
+ optimisticTargetSeat.user?.id != currentUserId) {
return null;
}
final incomingTargetSeat = nextMap[targetIndex];
if (incomingTargetSeat?.user != null &&
- !_userProfileMatchesAnyIdentity(
- incomingTargetSeat?.user,
- currentUserKeys,
- )) {
+ incomingTargetSeat?.user?.id != currentUserId) {
return null;
}
@@ -1592,12 +1544,13 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
MicRes? _currentUserMicSeat() {
- final currentUserKeys = _currentUserIdentityKeys();
- if (currentUserKeys.isEmpty) {
+ final currentUserId =
+ (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
+ if (currentUserId.isEmpty) {
return null;
}
for (final mic in roomWheatMap.values) {
- if (_userProfileMatchesAnyIdentity(mic.user, currentUserKeys)) {
+ if ((mic.user?.id ?? "").trim() == currentUserId) {
return mic;
}
}
@@ -1605,8 +1558,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
void _syncSelfMicRuntimeState() {
- final currentUserKeys = _currentUserIdentityKeys();
- if (currentUserKeys.isEmpty) {
+ final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id;
+ if ((currentUserId ?? "").isEmpty) {
return;
}
final roomId = currenRoom?.roomProfile?.roomProfile?.id ?? "";
@@ -2458,10 +2411,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
bool keepAudioPublishing = false,
}) async {
if (!_roomRtcJoined || _roomRtcEngineAdapter == null) {
- debugPrint(
- '[RoomRtc] switchToBroadcaster skipped joined=$_roomRtcJoined '
- 'hasAdapter=${_roomRtcEngineAdapter != null}',
- );
return false;
}
try {
@@ -2470,7 +2419,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
RoomRtcAnchorConfig(muted: muted, publishMuted: localAudioMuted),
);
if (!switched) {
- debugPrint('[RoomRtc] switchToBroadcaster failed adapter=false');
_roomRtcRoleIsBroadcaster = false;
_resyncHeartbeatFromRoomRtcState();
return false;
@@ -2483,7 +2431,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_resyncHeartbeatFromRoomRtcState();
return true;
} catch (error) {
- debugPrint('[RoomRtc] switchToBroadcaster error=$error');
_roomRtcRoleIsBroadcaster = false;
_resyncHeartbeatFromRoomRtcState();
return false;
@@ -2551,14 +2498,13 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
Future setSelfMicMutedFromBottom(bool muted) async {
- _notifySelfMicUiState(muted: muted);
+ isMic = muted;
+ notifyListeners();
final currentUserId =
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
- final seatIndex =
- currentUserId.isNotEmpty
- ? userOnMaiInIndex(currentUserId)
- : (_currentUserMicSeat()?.micIndex ?? -1);
+ final seatIndex = userOnMaiInIndex(currentUserId);
if (seatIndex < 0) {
+ notifyListeners();
return;
}
final seat = roomWheatMap[seatIndex];
@@ -2576,27 +2522,18 @@ class RealTimeCommunicationManager extends ChangeNotifier {
notifyListeners();
}
- Future toggleSelfMicMutedFromBottom() {
- return setSelfMicMutedFromBottom(!isMic);
- }
-
Future handleSelfMicRemovedByRemote({
bool refreshMicList = true,
}) async {
- final currentUserKeys = _currentUserIdentityKeys();
final currentUserId =
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
- if (currentUserKeys.isEmpty) {
- _notifySelfMicUiState(muted: true, notifyIfUnchanged: true);
+ if (currentUserId.isEmpty) {
await _switchRoomRtcToAudience();
return;
}
- final currentSeatIndex =
- currentUserId.isNotEmpty
- ? userOnMaiInIndex(currentUserId)
- : (_currentUserMicSeat()?.micIndex ?? -1);
- _setSelfMicMuted(true);
+ final currentSeatIndex = userOnMaiInIndex(currentUserId);
+ isMic = true;
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
if (currentSeatIndex > -1) {
@@ -2604,11 +2541,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
} else {
_clearSelfMicReleaseGuard();
}
- _clearCurrentUserFromSeats();
+ _clearUserFromSeats(currentUserId);
_roomRtcRoleIsBroadcaster = false;
_syncSelfMicRuntimeState();
- _clearConfirmedSelfMicIdentity();
- _notifySelfMicUiState(notifyIfUnchanged: true);
+ notifyListeners();
await _switchRoomRtcToAudience();
if (refreshMicList) {
@@ -4125,63 +4061,25 @@ class RealTimeCommunicationManager extends ChangeNotifier {
if (resolvedRoomId.isEmpty) {
return;
}
- _roomRocketGiftRefreshRoomId = resolvedRoomId;
- _roomRocketGiftRefreshDirty = true;
- if (_roomRocketGiftRefreshTimer != null || _roomRocketGiftRefreshInFlight) {
- return;
- }
- _roomRocketGiftRefreshTimer = Timer(
- _roomRocketGiftRefreshThrottleDelay,
- () => unawaited(_runRoomRocketGiftStatusRefresh()),
- );
- }
-
- void _schedulePendingRoomRocketGiftStatusRefreshIfNeeded() {
- if (!_roomRocketGiftRefreshDirty || _roomRocketGiftRefreshTimer != null) {
- return;
- }
- final pendingRoomId = (_roomRocketGiftRefreshRoomId ?? "").trim();
- final currentRoomId =
- (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
- if (pendingRoomId.isEmpty || pendingRoomId != currentRoomId) {
- return;
- }
- _roomRocketGiftRefreshTimer = Timer(
- _roomRocketGiftRefreshThrottleDelay,
- () => unawaited(_runRoomRocketGiftStatusRefresh()),
- );
- }
-
- Future _runRoomRocketGiftStatusRefresh() async {
- _roomRocketGiftRefreshTimer = null;
- if (_roomRocketGiftRefreshInFlight) {
- return;
- }
- final resolvedRoomId = (_roomRocketGiftRefreshRoomId ?? "").trim();
- if (resolvedRoomId.isEmpty) {
- _roomRocketGiftRefreshDirty = false;
- return;
- }
- _roomRocketGiftRefreshDirty = false;
- if (resolvedRoomId !=
- (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) {
- _schedulePendingRoomRocketGiftStatusRefreshIfNeeded();
- return;
- }
- _roomRocketGiftRefreshInFlight = true;
- try {
- await refreshRoomRocketStatus(roomId: resolvedRoomId);
- } finally {
- _roomRocketGiftRefreshInFlight = false;
- _schedulePendingRoomRocketGiftStatusRefreshIfNeeded();
+ _cancelRoomRocketGiftStatusRefresh();
+ for (final delay in _roomRocketGiftRefreshDelays) {
+ final timer = Timer(delay, () {
+ _roomRocketGiftRefreshTimers.removeWhere((item) => !item.isActive);
+ if (resolvedRoomId !=
+ (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) {
+ return;
+ }
+ unawaited(refreshRoomRocketStatus(roomId: resolvedRoomId));
+ });
+ _roomRocketGiftRefreshTimers.add(timer);
}
}
void _cancelRoomRocketGiftStatusRefresh() {
- _roomRocketGiftRefreshTimer?.cancel();
- _roomRocketGiftRefreshTimer = null;
- _roomRocketGiftRefreshRoomId = null;
- _roomRocketGiftRefreshDirty = false;
+ for (final timer in _roomRocketGiftRefreshTimers) {
+ timer.cancel();
+ }
+ _roomRocketGiftRefreshTimers.clear();
}
void _scheduleRoomRocketEntryReadyFallback(String roomId) {
@@ -4425,8 +4323,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
SCGiftVapSvgaManager().play(
animationUrl,
- priority: SCGiftVapSvgaManager.rocketLaunchEffectPriority,
- type: SCGiftVapSvgaManager.rocketLaunchEffectType,
+ priority: SCGiftVapSvgaManager.entryEffectPriority + 100,
+ type: SCGiftVapSvgaManager.entryEffectType,
);
}
@@ -5133,7 +5031,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
_clearSelfMicReleaseGuard();
- _clearConfirmedSelfMicIdentity();
_seatResizeOccupantPreserveUntilMs = null;
roomIsMute = false;
rtmProvider?.roomAllMsgList.clear();
@@ -5143,7 +5040,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
_roomVisualEffectsEnabled = false;
_voiceRoomRouteVisible = false;
_isExitingCurrentVoiceRoomSession = false;
- _setSelfMicMuted(true);
+ isMic = true;
isMusicPlaying = false;
if (clearPersistedRoomMarker) {
DataPersistence.setLastTimeRoomId("");
@@ -5197,10 +5094,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
if (clickUser.id ==
AccountStorage().getCurrentUser()?.userProfile?.id) {
///是自己,直接打开资料卡
- showBottomInBottomDialog(
- context!,
- RoomUserInfoCard(userId: clickUser.id),
- );
+ RoomUserInfoCard.showPreloaded(context!, userId: clickUser.id);
} else {
showBottomInBottomDialog(
context!,
@@ -5292,159 +5186,25 @@ class RealTimeCommunicationManager extends ChangeNotifier {
).catchError((_) {});
}
- void _clearCurrentUserFromSeats({num? exceptIndex}) {
- _clearUsersFromSeatsByIdentityKeys(
- _currentUserIdentityKeys(),
- exceptIndex: exceptIndex,
- );
- }
-
- void _clearUsersFromSeatsByIdentityKeys(
- Set lookupKeys, {
- num? exceptIndex,
- }) {
- lookupKeys.removeWhere((item) => item.trim().isEmpty);
- if (lookupKeys.isEmpty) {
+ void _clearUserFromSeats(String? userId, {num? exceptIndex}) {
+ final normalizedUserId = (userId ?? "").trim();
+ if (normalizedUserId.isEmpty) {
return;
}
roomWheatMap.forEach((seatIndex, seat) {
- final seatUserKeys = _userProfileIdentityKeys(seat.user);
- if (!seatUserKeys.any(lookupKeys.contains) || seatIndex == exceptIndex) {
+ if (seat.user?.id != normalizedUserId || seatIndex == exceptIndex) {
return;
}
roomWheatMap[seatIndex] = seat.copyWith(clearUser: true);
});
}
- void _applySelfMicGoUpState({
- required SocialChatUserProfile myUser,
- required num targetIndex,
- required SCMicGoUpRes micGoUpRes,
- required bool selfMicMuted,
- }) {
- _rememberConfirmedSelfMicIdentity(myUser);
- final previousSelfSeatIndex = userOnMaiInIndex(myUser.id ?? "");
- final isSeatSwitching =
- previousSelfSeatIndex > -1 && previousSelfSeatIndex != targetIndex;
-
- _clearSelfMicReleaseGuard();
- if (isSeatSwitching) {
- _startSelfMicSwitchGuard(
- sourceIndex: previousSelfSeatIndex,
- targetIndex: targetIndex,
- );
- } else {
- _clearSelfMicSwitchGuard();
- _startSelfMicGoUpGuard(targetIndex: targetIndex);
- }
- _clearCurrentUserFromSeats(exceptIndex: targetIndex);
-
- final currentSeat = roomWheatMap[targetIndex];
- final currentUser = myUser.copyWith(roles: currenRoom?.entrants?.roles);
- roomWheatMap[targetIndex] =
- currentSeat?.copyWith(
- user: currentUser,
- micMute: micGoUpRes.micMute,
- micLock: micGoUpRes.micLock,
- roomToken: micGoUpRes.roomToken,
- ) ??
- MicRes(
- roomId: currenRoom?.roomProfile?.roomProfile?.id,
- micIndex: targetIndex,
- micLock: micGoUpRes.micLock,
- micMute: micGoUpRes.micMute,
- user: currentUser,
- roomToken: micGoUpRes.roomToken,
- );
- _setSelfMicMuted(selfMicMuted);
- _syncSelfMicRuntimeState();
- }
-
- SocialChatUserProfile? _resolveSelfMicGoUpUser({
- required SocialChatUserProfile? localUser,
- required SCMicGoUpRes micGoUpRes,
- }) {
- final responseUser = _profileFromMicGoUpUser(micGoUpRes.user);
- if (localUser == null) {
- return responseUser;
- }
- if (responseUser == null) {
- return localUser.copyWith(roles: currenRoom?.entrants?.roles);
- }
- return localUser.copyWith(
- id: _firstNonBlankText([responseUser.id, localUser.id]),
- account: _firstNonBlankText([responseUser.account, localUser.account]),
- userAvatar: _firstNonBlankText([
- localUser.userAvatar,
- responseUser.userAvatar,
- ]),
- userNickname: _firstNonBlankText([
- localUser.userNickname,
- responseUser.userNickname,
- ]),
- roles: _firstNonBlankText([
- localUser.roles,
- responseUser.roles,
- currenRoom?.entrants?.roles,
- ]),
- charmLevel: localUser.charmLevel ?? responseUser.charmLevel,
- heartbeatVal: localUser.heartbeatVal ?? responseUser.heartbeatVal,
- userSex: localUser.userSex ?? responseUser.userSex,
- );
- }
-
- SocialChatUserProfile? _profileFromMicGoUpUser(User? user) {
- if (user == null) {
- return null;
- }
- final id = _firstNonBlankText([user.id]);
- final account = _firstNonBlankText([user.account]);
- if (id == null && account == null) {
- return null;
- }
- return SocialChatUserProfile(
- id: id,
- account: account,
- charmLevel: user.charmLevel,
- heartbeatVal: user.heartbeatVal,
- roles: _firstNonBlankText([user.roles, currenRoom?.entrants?.roles]),
- userAvatar: _firstNonBlankText([user.userAvatar]),
- userNickname: _firstNonBlankText([user.userNickname, user.account]),
- userSex: user.userSex,
- );
- }
-
- @visibleForTesting
- void debugApplySelfMicGoUpResponse({
- SocialChatUserProfile? localUser,
- required num targetIndex,
- required SCMicGoUpRes micGoUpRes,
- }) {
- final selfUser = _resolveSelfMicGoUpUser(
- localUser: localUser,
- micGoUpRes: micGoUpRes,
- );
- if (selfUser == null) {
- return;
- }
- _applySelfMicGoUpState(
- myUser: selfUser,
- targetIndex: targetIndex,
- micGoUpRes: micGoUpRes,
- selfMicMuted: micGoUpRes.micMute ?? false,
- );
- notifyListeners();
- }
-
void _openRoomUserInfoCard(String? userId) {
final normalizedUserId = (userId ?? '').trim();
if (normalizedUserId.isEmpty) {
return;
}
- showBottomInBottomDialog(
- context!,
- RoomUserInfoCard(userId: normalizedUserId),
- );
+ RoomUserInfoCard.showPreloaded(context!, userId: normalizedUserId);
}
addSoundVoiceChangeListener(OnSoundVoiceChange onSoundVoiceChange) {
@@ -5499,35 +5259,66 @@ class RealTimeCommunicationManager extends ChangeNotifier {
final targetIndex = micGoUpRes.micIndex ?? index;
final selfMicMuted = micGoUpRes.micMute ?? false;
- final selfUser = _resolveSelfMicGoUpUser(
- localUser: myUser,
- micGoUpRes: micGoUpRes,
- );
- if (selfUser != null) {
- _applySelfMicGoUpState(
- myUser: selfUser,
- targetIndex: targetIndex,
- micGoUpRes: micGoUpRes,
- selfMicMuted: selfMicMuted,
- );
- notifyListeners();
- } else {
- _refreshMicListSilently(notifyIfUnchanged: true);
- }
-
final switched = await _switchRoomRtcToBroadcaster(
muted: selfMicMuted,
keepAudioPublishing: selfMicMuted && _currentUserPublishingRoomMusic,
);
if (!switched) {
+ await _switchRoomRtcToAudience();
+ try {
+ await SCChatRoomRepository().micGoDown(
+ currenRoom?.roomProfile?.roomProfile?.id ?? "",
+ targetIndex,
+ );
+ } catch (downError) {}
+ _clearSelfMicSwitchGuard(clearPreferredIndex: true);
+ _clearSelfMicGoUpGuard(clearPreferredIndex: true);
+ _clearUserFromSeats(myUser?.id);
+ _syncSelfMicRuntimeState();
+ notifyListeners();
_refreshMicListSilently(notifyIfUnchanged: true);
- _resetSelfMicGoUpFailureState();
- SCTts.show(
- 'Microphone audio failed to start, please try switching microphone again',
+ _handleSelfMicGoUpFailure(
+ 'Failed to put on the microphone, TRTC switch failed',
);
return;
}
+ final previousSelfSeatIndex =
+ myUser != null ? userOnMaiInIndex(myUser.id ?? "") : -1;
+ final isSeatSwitching =
+ previousSelfSeatIndex > -1 && previousSelfSeatIndex != targetIndex;
+ if (myUser != null) {
+ _clearSelfMicReleaseGuard();
+ if (isSeatSwitching) {
+ _startSelfMicSwitchGuard(
+ sourceIndex: previousSelfSeatIndex,
+ targetIndex: targetIndex,
+ );
+ } else {
+ _clearSelfMicSwitchGuard();
+ _startSelfMicGoUpGuard(targetIndex: targetIndex);
+ }
+ _clearUserFromSeats(myUser.id, exceptIndex: targetIndex);
+ final currentSeat = roomWheatMap[targetIndex];
+ final currentUser = myUser.copyWith(roles: currenRoom?.entrants?.roles);
+ roomWheatMap[targetIndex] =
+ currentSeat?.copyWith(
+ user: currentUser,
+ micMute: micGoUpRes.micMute,
+ micLock: micGoUpRes.micLock,
+ roomToken: micGoUpRes.roomToken,
+ ) ??
+ MicRes(
+ roomId: currenRoom?.roomProfile?.roomProfile?.id,
+ micIndex: targetIndex,
+ micLock: micGoUpRes.micLock,
+ micMute: micGoUpRes.micMute,
+ user: currentUser,
+ roomToken: micGoUpRes.roomToken,
+ );
+ }
+ isMic = selfMicMuted;
+ _syncSelfMicRuntimeState();
if (roomWheatMap[targetIndex]?.micMute ?? false) {
///房主上麦自动解禁麦位
if (isFz()) {
@@ -5565,16 +5356,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id ?? "";
- final currentUserKeys = _currentUserIdentityKeys();
- final currentUserSeatIndex =
- currentUserId.trim().isNotEmpty
- ? userOnMaiInIndex(currentUserId)
- : (_currentUserMicSeat()?.micIndex ?? -1);
- if (_userProfileMatchesAnyIdentity(
- roomWheatMap[index]?.user,
- currentUserKeys,
- )) {
- _setSelfMicMuted(true);
+ final currentUserSeatIndex = userOnMaiInIndex(currentUserId);
+ if (roomWheatMap[index]?.user?.id == currentUserId) {
+ isMic = true;
}
_clearSelfMicSwitchGuard(clearPreferredIndex: true);
_clearSelfMicGoUpGuard(clearPreferredIndex: true);
@@ -5585,10 +5369,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
}
SCHeartbeatUtils.cancelAnchorTimer();
- _clearCurrentUserFromSeats();
+ await _switchRoomRtcToAudience();
+ _clearUserFromSeats(currentUserId);
_syncSelfMicRuntimeState();
- _clearConfirmedSelfMicIdentity();
- _notifySelfMicUiState(notifyIfUnchanged: true);
+ notifyListeners();
requestMicrophoneListRefresh(
notifyIfUnchanged: false,
minInterval: const Duration(milliseconds: 350),
@@ -5998,10 +5782,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
GlobalKey>? getSeatGlobalKeyByIndex(String userId) {
///需要有人的座位
- num index = _findUserOnMaiInIndex(
- userId,
- useDisplaySeatForCurrentUser: false,
- );
+ num index = userOnMaiInIndex(userId);
if (index > -1) {
return seatGlobalKeyMap[index];
}
diff --git a/lib/services/audio/rtm_manager.dart b/lib/services/audio/rtm_manager.dart
index 417de03..e01ff20 100644
--- a/lib/services/audio/rtm_manager.dart
+++ b/lib/services/audio/rtm_manager.dart
@@ -14,7 +14,6 @@ import 'package:yumi/shared/tools/sc_message_utils.dart';
import 'package:yumi/shared/tools/sc_path_utils.dart';
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
import 'package:yumi/shared/tools/sc_room_utils.dart';
-import 'package:yumi/shared/tools/sc_chat_bubble_message_utils.dart';
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
import 'package:yumi/shared/tools/sc_cp_relation_broadcast_assets.dart';
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
@@ -41,7 +40,6 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_image_elem.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
-import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_extension.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_msg_create_info_result.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
@@ -278,33 +276,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
].join("|");
}
- bool _shouldNotifyFloatingGiftListener(Msg msg) {
- if (msg.type == SCRoomMsgType.gift) {
- return true;
- }
- if (msg.type == SCRoomMsgType.luckGiftAnimOther) {
- return msg.user != null && msg.gift != null;
- }
- return false;
- }
-
- void _notifyFloatingGiftListenerFromAddMsg(Msg msg) {
- if (!_shouldNotifyFloatingGiftListener(msg)) {
- return;
- }
- _giftFxLog(
- 'trigger floating gift listener from addMsg '
- 'type=${msg.type} '
- 'giftId=${msg.gift?.id} '
- 'sendUserId=${msg.user?.id} '
- 'toUserId=${msg.toUser?.id} '
- 'quantity=${msg.number} '
- 'targetUserIds=${(msg.targetUserIds ?? const []).join(",")} '
- 'hasListener=${msgFloatingGiftListener != null}',
- );
- msgFloatingGiftListener?.call(msg);
- }
-
String _firstNonBlank(Iterable values) {
for (final value in values) {
final text = value?.trim();
@@ -2645,18 +2616,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (createTextMessageRes.code == 0) {
// 文本信息创建成功
String id = createTextMessageRes.data!.id!;
- final chatBubble =
- AccountStorage().getCurrentUser()?.userProfile?.getChatBox();
- final chatBubbleCloudCustomData = scEncodeChatBubbleCloudCustomData(
- chatBubble,
- );
- final chatBubbleSnapshotValue = scEncodeChatBubbleSnapshotValue(
- chatBubble,
- );
// 发送文本消息
// 在sendMessage时,若只填写receiver则发个人用户单聊消息
// 若只填写groupID则发群组消息
// 若填写了receiver与groupID则发群内的个人用户,消息在群聊中显示,只有指定receiver能看见
+ String receiveId = toConversation.userID!;
V2TimValueCallback sendMessageRes = await TencentImSDKPlugin
.v2TIMManager
.getMessageManager()
@@ -2665,20 +2629,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
receiver: toConversation.userID!, // 接收人id
needReadReceipt: true,
groupID: '', // 是否需要已读回执
- isSupportMessageExtension: chatBubbleSnapshotValue != null,
- cloudCustomData: chatBubbleCloudCustomData,
);
if (sendMessageRes.code == 0) {
// 发送成功
_onNew1v1Message(sendMessageRes.data);
- if (chatBubbleSnapshotValue != null) {
- unawaited(
- _setC2CChatBubbleMessageExtension(
- sendMessageRes.data?.msgID,
- chatBubbleSnapshotValue,
- ),
- );
- }
} else {
SCTts.show(
'create fail,code:${sendMessageRes.code},${sendMessageRes.desc}',
@@ -2687,33 +2641,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
}
}
- Future _setC2CChatBubbleMessageExtension(
- String? msgID,
- String snapshotValue,
- ) async {
- final id = msgID?.trim();
- if (id == null || id.isEmpty) {
- return;
- }
- try {
- await TencentImSDKPlugin.v2TIMManager
- .getMessageManager()
- .setMessageExtensions(
- msgID: id,
- extensions: [
- V2TimMessageExtension(
- extensionKey: scChatBubbleMessageExtensionKey,
- extensionValue: snapshotValue,
- ),
- ],
- );
- } catch (error) {
- if (kDebugMode) {
- debugPrint('[chat_bubble] set extension failed msgID=$id error=$error');
- }
- }
- }
-
///发送单聊自定义消息
Future sendC2CCustomMsg(
String msg,
@@ -3050,7 +2977,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (mergedGiftMsg != null) {
msgAllListener?.call(mergedGiftMsg);
msgGiftListener?.call(mergedGiftMsg);
- _notifyFloatingGiftListenerFromAddMsg(msg);
+ if (msg.type == SCRoomMsgType.gift) {
+ msgFloatingGiftListener?.call(msg);
+ }
notifyListeners();
return;
}
@@ -3142,39 +3071,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
roomGiftMsgList.removeAt(roomGiftMsgList.length - 1);
}
msgGiftListener?.call(msg);
- _notifyFloatingGiftListenerFromAddMsg(msg);
- }
- }
-
- List _luckGiftTargetUserIdsFromMsg(Msg msg) {
- final targetUserIds = [];
- void addTargetUserId(dynamic userId) {
- final normalizedUserId = userId?.toString().trim() ?? '';
- if (normalizedUserId.isEmpty ||
- targetUserIds.contains(normalizedUserId)) {
- return;
+ if (msg.type == SCRoomMsgType.gift) {
+ msgFloatingGiftListener?.call(msg);
}
- targetUserIds.add(normalizedUserId);
}
-
- for (final userId in msg.targetUserIds ?? const []) {
- addTargetUserId(userId);
- }
-
- final rawTargetUserIds = (msg.msg ?? '').trim();
- if (rawTargetUserIds.startsWith('[')) {
- try {
- final decoded = jsonDecode(rawTargetUserIds);
- if (decoded is List) {
- for (final userId in decoded) {
- addTargetUserId(userId);
- }
- }
- } catch (_) {}
- }
-
- addTargetUserId(msg.toUser?.id);
- return targetUserIds;
}
void _enqueueCpRelationBroadcastFloatingIfNeeded(
@@ -5149,6 +5049,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
RoomRocketLaunchBroadcastMessage launch,
) {
_clearRoomRocketLaunchCountdownForRoom(launch.roomId);
+ _pendingRoomRocketRewardPopups.remove(launch.roomId);
+ _recentRoomRocketRewardProbeTimes.remove(launch.roomId);
_markRoomRocketLaunchEffectConsumed(launch);
}
@@ -6109,15 +6011,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
currentContext,
listen: false,
);
- if (_isCurrentVisibleVoiceRoom(launchRoomId)) {
- _markRoomRocketRewardProbe(launch.roomId);
- _playRoomRocketLaunchEffectsAndReward(launch);
- } else {
- rtcProvider.advanceRoomRocketStatusAfterLaunch(launch);
- rtcProvider.scheduleRoomRocketPostLaunchStatusRefresh(
- roomId: launchRoomId,
- );
- }
+ rtcProvider.advanceRoomRocketStatusAfterLaunch(launch);
+ rtcProvider.scheduleRoomRocketPostLaunchStatusRefresh(
+ roomId: launchRoomId,
+ );
}
}
_scheduleRoomRocketStatusRefreshForCurrentRoom(launchRoomId);
@@ -6593,7 +6490,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
'giftPhoto=${msg.gift?.giftPhoto}',
);
} else {
- final targetUserIds = _luckGiftTargetUserIdsFromMsg(msg);
+ final targetUserIds =
+ (jsonDecode(msg.msg ?? "") as List)
+ .map((e) => e as String)
+ .toList();
_giftFxLog(
'recv LUCK_GIFT_ANIM_OTHER '
'giftPhoto=${msg.gift?.giftPhoto} '
@@ -6608,7 +6508,16 @@ class RealTimeMessagingManager extends ChangeNotifier {
targetUserIds,
),
);
- if (!_shouldNotifyFloatingGiftListener(msg)) {
+ if (msg.user != null && msg.toUser != null && msg.gift != null) {
+ _giftFxLog(
+ 'trigger floating gift listener from LUCK_GIFT_ANIM_OTHER '
+ 'sendUserId=${msg.user?.id} '
+ 'toUserId=${msg.toUser?.id} '
+ 'quantity=${msg.number} '
+ 'giftId=${msg.gift?.id}',
+ );
+ msgFloatingGiftListener?.call(msg);
+ } else {
_giftFxLog(
'skip floating gift listener from LUCK_GIFT_ANIM_OTHER '
'reason=incomplete_msg '
diff --git a/lib/services/auth/authentication_manager.dart b/lib/services/auth/authentication_manager.dart
index 853725d..47fd382 100644
--- a/lib/services/auth/authentication_manager.dart
+++ b/lib/services/auth/authentication_manager.dart
@@ -1,7 +1,6 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import 'package:yumi/app/routes/sc_routes.dart';
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
@@ -105,60 +104,49 @@ class SocialChatAuthenticationManager extends ChangeNotifier {
} else if (authType == SCAuthType.APPLE.name) {
this.authType = authType;
try {
- _authDebugLog('apple sign-in start');
- final isAvailable = await SignInWithApple.isAvailable();
- _authDebugLog('apple sign-in availability=$isAvailable');
- if (!isAvailable) {
- SCTts.show("Sign in with Apple is not available on this device.");
- return;
- }
final appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [
- AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName, // 请求姓名权限
],
);
uid = appleCredential.userIdentifier ?? "";
- _authDebugLog(
- 'apple credential success userIdentifier=${_maskForDebug(uid)} '
- 'hasAuthorizationCode=${appleCredential.authorizationCode.isNotEmpty} '
- 'hasIdentityToken=${(appleCredential.identityToken ?? "").isNotEmpty} '
- 'hasEmail=${(appleCredential.email ?? "").isNotEmpty}',
- );
- if (uid.isEmpty) {
- SCTts.show("Apple authorization failed. Please try again.");
- return;
- }
SCLoadingManager.show();
- await DataPersistence.setPendingChannelAuth(authType, uid);
- var user = await SCAccountRepository().loginForChannel(authType, uid);
- if (!context.mounted) {
+ if (uid.isNotEmpty) {
+ await DataPersistence.setPendingChannelAuth(authType, uid);
+ var user = await SCAccountRepository().loginForChannel(authType, uid);
+ if (!context.mounted) {
+ SCLoadingManager.hide();
+ return;
+ }
+ AccountStorage().setCurrentUser(user);
+ await DataPersistence.clearPendingChannelAuth();
+ if (!context.mounted) {
+ SCLoadingManager.hide();
+ return;
+ }
+ final canContinue = await SCVersionUtils.checkReview(
+ context: context,
+ );
+ if (!context.mounted || !canContinue) {
+ SCLoadingManager.hide();
+ return;
+ }
+ await Provider.of(
+ context,
+ listen: false,
+ ).resetLocalRoomState(
+ fallbackRtmProvider: Provider.of(
+ context,
+ listen: false,
+ ),
+ );
+ if (!context.mounted) {
+ return;
+ }
SCLoadingManager.hide();
- return;
+ notifyListeners();
+ SCNavigatorUtils.push(context, SCRoutes.home, replace: true);
}
- AccountStorage().setCurrentUser(user);
- await DataPersistence.clearPendingChannelAuth();
- if (!context.mounted) {
- SCLoadingManager.hide();
- return;
- }
- final canContinue = await SCVersionUtils.checkReview(context: context);
- if (!context.mounted || !canContinue) {
- SCLoadingManager.hide();
- return;
- }
- await Provider.of(
- context,
- listen: false,
- ).resetLocalRoomState(
- fallbackRtmProvider: Provider.of(context, listen: false),
- );
- if (!context.mounted) {
- return;
- }
- SCLoadingManager.hide();
- notifyListeners();
- SCNavigatorUtils.push(context, SCRoutes.home, replace: true);
} catch (e) {
SCLoadingManager.hide();
_showAuthError(e);
@@ -174,56 +162,13 @@ class SocialChatAuthenticationManager extends ChangeNotifier {
}
void _showAuthError(Object error) {
- _authDebugLog('authenticateUser error: $error');
-
if (error is DioException) {
return;
}
- if (error is SignInWithAppleAuthorizationException) {
- _showAppleAuthorizationError(error);
- return;
- }
-
- if (error is SignInWithAppleNotSupportedException) {
- SCTts.show("Sign in with Apple is not available on this device.");
- return;
- }
-
final message = error.toString().replaceFirst("Exception: ", "").trim();
if (message.isNotEmpty) {
SCTts.show(message);
}
}
-
- void _showAppleAuthorizationError(
- SignInWithAppleAuthorizationException error,
- ) {
- if (error.code == AuthorizationErrorCode.canceled) {
- return;
- }
- if (error.code == AuthorizationErrorCode.unknown) {
- SCTts.show(
- "Apple sign-in is not available. Please check the app signing profile.",
- );
- return;
- }
- SCTts.show("Apple authorization failed. Please try again.");
- }
-
- void _authDebugLog(String message) {
- if (kDebugMode) {
- debugPrint('[Auth][Apple] $message');
- }
- }
-
- String _maskForDebug(String value) {
- if (value.isEmpty) {
- return '';
- }
- if (value.length <= 8) {
- return '***';
- }
- return '${value.substring(0, 4)}...${value.substring(value.length - 4)}';
- }
}
diff --git a/lib/services/auth/user_profile_manager.dart b/lib/services/auth/user_profile_manager.dart
index d5adb4e..6b640ee 100644
--- a/lib/services/auth/user_profile_manager.dart
+++ b/lib/services/auth/user_profile_manager.dart
@@ -250,7 +250,7 @@ class SocialChatUserProfileManager extends ChangeNotifier {
return sessionSerial;
}
- Future roomUserCard(
+ Future roomUserCard(
String roomId,
String userId, {
int? sessionSerial,
@@ -272,36 +272,31 @@ class SocialChatUserProfileManager extends ChangeNotifier {
isUserCardLoading = false;
notifyListeners();
}
- return;
+ return null;
}
try {
- RoomUserCardRes? card = await SCChatRoomRepository().roomUserCard(
- normalizedRoomId,
- normalizedUserId,
+ final card = await _loadHydratedRoomUserCard(
+ roomId: normalizedRoomId,
+ userId: normalizedUserId,
);
- if (requestSerial != _userCardRequestSerial) {
- return;
+ if (requestSerial != _userCardRequestSerial ||
+ activeSessionSerial != _activeUserCardSessionSerial) {
+ return null;
}
userCardInfo = card;
userCardLoadError = null;
isUserCardLoading = false;
- isUserCardRelationLoading = card.userProfile != null;
+ isUserCardRelationLoading = false;
notifyListeners();
- unawaited(
- _hydrateRoomUserCard(
- card: card,
- userId: normalizedUserId,
- requestSerial: requestSerial,
- sessionSerial: activeSessionSerial,
- ),
- );
+ return card;
} catch (error, stackTrace) {
if (requestSerial != _userCardRequestSerial) {
- return;
+ return null;
}
userCardInfo = null;
userCardLoadError = error;
+ isUserCardRelationLoading = false;
_debugProfileLoadFailure(
scope: 'roomCard',
userId: normalizedUserId,
@@ -314,6 +309,23 @@ class SocialChatUserProfileManager extends ChangeNotifier {
notifyListeners();
}
}
+ return null;
+ }
+
+ Future preloadRoomUserCardForOpen(
+ String roomId,
+ String userId, {
+ int? sessionSerial,
+ }) {
+ return roomUserCard(roomId, userId, sessionSerial: sessionSerial);
+ }
+
+ Future _loadHydratedRoomUserCard({
+ required String roomId,
+ required String userId,
+ }) async {
+ final card = await SCChatRoomRepository().roomUserCard(roomId, userId);
+ return _withCurrentVipLevelForCard(card, userId);
}
Future _hydrateUserInfoById({
@@ -340,38 +352,6 @@ class SocialChatUserProfileManager extends ChangeNotifier {
}
}
- Future _hydrateRoomUserCard({
- required RoomUserCardRes? card,
- required String userId,
- required int requestSerial,
- required int sessionSerial,
- }) async {
- try {
- final hydratedCard = await _withCurrentVipLevelForCard(card, userId);
- if (requestSerial != _userCardRequestSerial ||
- sessionSerial != _activeUserCardSessionSerial ||
- hydratedCard == null) {
- return;
- }
- userCardInfo = hydratedCard;
- userCardLoadError = null;
- isUserCardRelationLoading = false;
- notifyListeners();
- } catch (error, stackTrace) {
- if (requestSerial == _userCardRequestSerial &&
- sessionSerial == _activeUserCardSessionSerial) {
- isUserCardRelationLoading = false;
- notifyListeners();
- }
- _debugProfileLoadFailure(
- scope: 'roomCardHydrate',
- userId: userId,
- error: error,
- stackTrace: stackTrace,
- );
- }
- }
-
void clearRoomUserCard({int? sessionSerial}) {
if (sessionSerial != null &&
sessionSerial != _activeUserCardSessionSerial) {
diff --git a/lib/services/general/sc_app_general_manager.dart b/lib/services/general/sc_app_general_manager.dart
index 611c954..27d6b07 100644
--- a/lib/services/general/sc_app_general_manager.dart
+++ b/lib/services/general/sc_app_general_manager.dart
@@ -468,97 +468,14 @@ class SCAppGeneralManager extends ChangeNotifier {
}
Map> emojiByTab = {};
- List roomEmojiGroups = [];
- bool _isFetchingRoomEmojis = false;
- bool _hasRoomEmojisLoaded = false;
- bool _roomEmojisLoadedFromAll = false;
-
- bool get isFetchingRoomEmojis => _isFetchingRoomEmojis;
-
- bool get hasRoomEmojisLoaded => _hasRoomEmojisLoaded;
-
- bool get roomEmojisLoadedFromAll => _roomEmojisLoadedFromAll;
-
- bool get hasRemoteRoomEmojis =>
- roomEmojiGroups.any((group) => (group.emojis ?? const []).isNotEmpty);
///emoji表情
- Future emojiAll({bool forceRefresh = false}) async {
- if (_isFetchingRoomEmojis) {
- _roomEmojiDebugLog('skip load: request already in flight');
- return;
- }
- if (_hasRoomEmojisLoaded && !forceRefresh && _roomEmojisLoadedFromAll) {
- _roomEmojiDebugLog(
- 'skip load: cached groups=${roomEmojiGroups.length} '
- 'tabs=${emojiByTab.length} source=/material/emoji/all',
- );
- return;
- }
- if (_hasRoomEmojisLoaded && !forceRefresh && !_roomEmojisLoadedFromAll) {
- _roomEmojiDebugLog(
- 'reload: cached data was not loaded from temporary /all source',
- );
- }
-
- _isFetchingRoomEmojis = true;
- _roomEmojiDebugLog('load start forceRefresh=$forceRefresh');
- try {
- final roomEmojis = await SCChatRoomRepository().emojiAll();
- _roomEmojiDebugLog(
- 'load response source=/material/emoji/all '
- 'rawGroups=${roomEmojis.length} '
- 'rawEmojiTotal=${_countRoomEmojiItems(roomEmojis)}',
- );
- roomEmojiGroups =
- roomEmojis
- .where(
- (group) =>
- group.have == true && (group.emojis ?? const []).isNotEmpty,
- )
- .toList();
- final filteredNoAccess =
- roomEmojis.where((group) => group.have != true).length;
- final filteredEmptyGroups =
- roomEmojis
- .where(
- (group) =>
- group.have == true && (group.emojis ?? const []).isEmpty,
- )
- .length;
- emojiByTab.clear();
- for (final value in roomEmojiGroups) {
- final groupName =
- value.groupName?.trim().isNotEmpty == true
- ? value.groupName!.trim()
- : (value.groupCode ?? value.id ?? "");
- if (groupName.isEmpty) {
- continue;
- }
- emojiByTab[groupName] = value.emojis ?? [];
- }
- _hasRoomEmojisLoaded = true;
- _roomEmojisLoadedFromAll = true;
- _roomEmojiDebugLog(
- 'load success visibleGroups=${roomEmojiGroups.length} '
- 'filteredNoAccess=$filteredNoAccess '
- 'filteredEmptyGroups=$filteredEmptyGroups '
- 'tabs=${emojiByTab.length} '
- 'visibleEmojiTotal=${_countRoomEmojiItems(roomEmojiGroups)} '
- 'sample=${_roomEmojiGroupSample(roomEmojiGroups)}',
- );
- } catch (error, stackTrace) {
- _roomEmojiDebugLog('load failed error=$error');
- debugPrintStack(
- label: '[RoomEmoji] load stack',
- stackTrace: stackTrace,
- maxFrames: 8,
- );
- return;
- } finally {
- _isFetchingRoomEmojis = false;
- notifyListeners();
+ void emojiAll() async {
+ var roomEmojis = await SCChatRoomRepository().emojiAll();
+ for (var value in roomEmojis) {
+ emojiByTab[value.groupName!] = value.emojis ?? [];
}
+ notifyListeners();
}
///加载等级资源
@@ -578,31 +495,3 @@ class SCAppGeneralManager extends ChangeNotifier {
}
}
}
-
-void _roomEmojiDebugLog(String message) {
- debugPrint('[RoomEmoji] $message');
-}
-
-int _countRoomEmojiItems(List groups) {
- return groups.fold(
- 0,
- (total, group) => total + (group.emojis?.length ?? 0),
- );
-}
-
-String _roomEmojiGroupSample(List groups) {
- if (groups.isEmpty) {
- return '[]';
- }
- return groups
- .take(3)
- .map((group) {
- final emojis = group.emojis ?? const [];
- final first = emojis.isNotEmpty ? emojis.first : null;
- return '{id=${group.id}, code=${group.groupCode}, name=${group.groupName}, '
- 'have=${group.have}, amount=${group.amount}, count=${emojis.length}, '
- 'firstType=${first?.sourceType}, firstSource=${first?.sourceUrl}, '
- 'firstCover=${first?.coverUrl}}';
- })
- .join(', ');
-}
diff --git a/lib/services/payment/google_payment_manager.dart b/lib/services/payment/google_payment_manager.dart
index 08b30d7..fc097df 100644
--- a/lib/services/payment/google_payment_manager.dart
+++ b/lib/services/payment/google_payment_manager.dart
@@ -15,7 +15,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/ui_kit/components/dialog/dialog_base.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_config_repository_imp.dart';
-import 'package:yumi/shared/business_logic/models/res/sc_google_pay_res.dart';
import 'package:yumi/shared/business_logic/models/res/sc_product_config_res.dart';
import 'package:yumi/modules/wallet/recharge/recharge_page.dart';
@@ -346,7 +345,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
return;
}
- // 2. 正常购买流依赖 pendingCompletePurchase;启动/回前台补偿流需要允许
+ // 2. 正常购买流会依赖 pendingCompletePurchase;启动/回前台补偿流必须允许
// 已确认但仍未消费的一次性商品再次上报后端,否则用户付款后退出 App 会漏单。
if (!forceSubmit && !purchase.pendingCompletePurchase) {
return;
@@ -368,7 +367,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
}
// 4. 服务器验证
- await _submitGooglePayVerification(
+ await SCConfigRepositoryImp().googlePay(
purchase.productID,
signature,
purchaseData,
@@ -457,7 +456,7 @@ class AndroidPaymentProcessor extends ChangeNotifier {
final isUnacknowledged =
purchase.pendingCompletePurchase ||
!purchase.billingClientPurchase.isAcknowledged;
- // Google 只会返回未消费的一次性商品;即使它已确认,也说明仍可补上报后端。
+ // Google 只会返回未消费的一次性商品;即使它已被确认,也说明仍可补上报后端。
return isPurchased &&
(isUnacknowledged || productType == 'consumable');
}).toList();
@@ -477,38 +476,6 @@ class AndroidPaymentProcessor extends ChangeNotifier {
}
}
- Future _submitGooglePayVerification(
- String product,
- String signature,
- String purchaseData,
- ) async {
- const api = 'order/purchase-pay/google';
- final paramsForLog = {
- 'product': product,
- 'signature': _clipForDebugLog(signature),
- 'purchaseData': _clipForDebugLog(purchaseData),
- };
- if (kDebugMode) {
- debugPrint('[GooglePay] request api=$api params=$paramsForLog');
- }
- final result = await SCConfigRepositoryImp().googlePay(
- product,
- signature,
- purchaseData,
- );
- if (kDebugMode) {
- debugPrint('[GooglePay] response api=$api result=${result.toJson()}');
- }
- return result;
- }
-
- String _clipForDebugLog(String value) {
- if (value.length <= 240) {
- return value;
- }
- return '${value.substring(0, 120)}...${value.substring(value.length - 80)}';
- }
-
// 交付商品
Future _deliverProduct(String productId) async {
// 实现您的业务逻辑
diff --git a/lib/shared/business_logic/models/res/sc_room_emoji_res.dart b/lib/shared/business_logic/models/res/sc_room_emoji_res.dart
index e061819..92775f4 100644
--- a/lib/shared/business_logic/models/res/sc_room_emoji_res.dart
+++ b/lib/shared/business_logic/models/res/sc_room_emoji_res.dart
@@ -1,197 +1,146 @@
-// id : 0
-// sysOrigin : ""
-// groupCode : ""
-// groupName : ""
-// cover : ""
-// have : false
-// amount : 0.0
-// emojis : [{"sourceUrl":"","sourceType":0,"coverUrl":"","type":""}]
-
-class SCRoomEmojiRes {
- SCRoomEmojiRes({
- String? id,
- String? sysOrigin,
- String? groupCode,
- String? groupName,
- String? cover,
- bool? have,
- num? amount,
- List? emojis,
- }) {
- _id = id;
- _sysOrigin = sysOrigin;
- _groupCode = groupCode;
- _groupName = groupName;
- _cover = cover;
- _have = have;
- _amount = amount;
- _emojis = emojis;
- }
-
- SCRoomEmojiRes.fromJson(dynamic json) {
- final map = json is Map ? json : const {};
- _id = _stringOrNull(map['id']);
- _sysOrigin = _stringOrNull(map['sysOrigin']);
- _groupCode = _stringOrNull(map['groupCode']);
- _groupName = _stringOrNull(map['groupName']);
- _cover = _stringOrNull(map['cover']);
- _have = _boolOrNull(map['have']);
- _amount = _numOrNull(map['amount']);
- if (map['emojis'] is List) {
- _emojis = [];
- map['emojis'].forEach((v) {
- _emojis?.add(Emojis.fromJson(v));
- });
- }
- }
-
- String? _id;
- String? _sysOrigin;
- String? _groupCode;
- String? _groupName;
- String? _cover;
- bool? _have;
- num? _amount;
- List? _emojis;
- SCRoomEmojiRes copyWith({
- String? id,
- String? sysOrigin,
- String? groupCode,
- String? groupName,
- String? cover,
- bool? have,
- num? amount,
- List? emojis,
- }) => SCRoomEmojiRes(
- id: id ?? _id,
- sysOrigin: sysOrigin ?? _sysOrigin,
- groupCode: groupCode ?? _groupCode,
- groupName: groupName ?? _groupName,
- cover: cover ?? _cover,
- have: have ?? _have,
- amount: amount ?? _amount,
- emojis: emojis ?? _emojis,
- );
-
- String? get id => _id;
- String? get sysOrigin => _sysOrigin;
- String? get groupCode => _groupCode;
- String? get groupName => _groupName;
- String? get cover => _cover;
- bool? get have => _have;
- num? get amount => _amount;
- List? get emojis => _emojis;
-
- Map toJson() {
- final map = {};
- map['id'] = _id;
- map['sysOrigin'] = _sysOrigin;
- map['groupCode'] = _groupCode;
- map['groupName'] = _groupName;
- map['cover'] = _cover;
- map['have'] = _have;
- map['amount'] = _amount;
- if (_emojis != null) {
- map['emojis'] = _emojis?.map((v) => v.toJson()).toList();
- }
- return map;
- }
-}
-
-/// sourceUrl : ""
-/// sourceType : 0
-/// coverUrl : ""
-/// type : ""
-
-class Emojis {
- Emojis({String? sourceUrl, num? sourceType, String? coverUrl, String? type}) {
- _sourceUrl = sourceUrl;
- _sourceType = sourceType;
- _coverUrl = coverUrl;
- _type = type;
- }
-
- Emojis.fromJson(dynamic json) {
- final map = json is Map ? json : const {};
- _sourceUrl = _stringOrNull(map['sourceUrl']);
- _sourceType = _numOrNull(map['sourceType']);
- _coverUrl = _stringOrNull(map['coverUrl']);
- _type = _stringOrNull(map['type']);
- }
-
- String? _sourceUrl;
- num? _sourceType;
- String? _coverUrl;
- String? _type;
- Emojis copyWith({
- String? sourceUrl,
- num? sourceType,
- String? coverUrl,
- String? type,
- }) => Emojis(
- sourceUrl: sourceUrl ?? _sourceUrl,
- sourceType: sourceType ?? _sourceType,
- coverUrl: coverUrl ?? _coverUrl,
- type: type ?? _type,
- );
-
- String? get sourceUrl => _sourceUrl;
- num? get sourceType => _sourceType;
- String? get coverUrl => _coverUrl;
- String? get type => _type;
- String get previewUrl {
- final cover = _coverUrl?.trim() ?? "";
- if (cover.isNotEmpty) {
- return cover;
- }
- return _sourceUrl?.trim() ?? "";
- }
-
- String get sendUrl => _sourceUrl?.trim() ?? previewUrl;
-
- Map toJson() {
- final map = {};
- map['sourceUrl'] = _sourceUrl;
- map['sourceType'] = _sourceType;
- map['coverUrl'] = _coverUrl;
- map['type'] = _type;
- return map;
- }
-}
-
-String? _stringOrNull(dynamic value) {
- if (value == null) {
- return null;
- }
- final text = value.toString();
- return text.isEmpty ? null : text;
-}
-
-num? _numOrNull(dynamic value) {
- if (value is num) {
- return value;
- }
- if (value is String) {
- return num.tryParse(value);
- }
- return null;
-}
-
-bool? _boolOrNull(dynamic value) {
- if (value is bool) {
- return value;
- }
- if (value is num) {
- return value != 0;
- }
- if (value is String) {
- final normalized = value.trim().toLowerCase();
- if (normalized == 'true' || normalized == '1') {
- return true;
- }
- if (normalized == 'false' || normalized == '0') {
- return false;
- }
- }
- return null;
-}
+/// id : 0
+/// sysOrigin : ""
+/// groupCode : ""
+/// groupName : ""
+/// cover : ""
+/// have : false
+/// amount : 0.0
+/// emojis : [{"sourceUrl":"","sourceType":0,"coverUrl":"","type":""}]
+
+class SCRoomEmojiRes {
+ SCRoomEmojiRes({
+ String? id,
+ String? sysOrigin,
+ String? groupCode,
+ String? groupName,
+ String? cover,
+ bool? have,
+ num? amount,
+ List? emojis,}){
+ _id = id;
+ _sysOrigin = sysOrigin;
+ _groupCode = groupCode;
+ _groupName = groupName;
+ _cover = cover;
+ _have = have;
+ _amount = amount;
+ _emojis = emojis;
+}
+
+ SCRoomEmojiRes.fromJson(dynamic json) {
+ _id = json['id'];
+ _sysOrigin = json['sysOrigin'];
+ _groupCode = json['groupCode'];
+ _groupName = json['groupName'];
+ _cover = json['cover'];
+ _have = json['have'];
+ _amount = json['amount'];
+ if (json['emojis'] != null) {
+ _emojis = [];
+ json['emojis'].forEach((v) {
+ _emojis?.add(Emojis.fromJson(v));
+ });
+ }
+ }
+ String? _id;
+ String? _sysOrigin;
+ String? _groupCode;
+ String? _groupName;
+ String? _cover;
+ bool? _have;
+ num? _amount;
+ List? _emojis;
+SCRoomEmojiRes copyWith({ String? id,
+ String? sysOrigin,
+ String? groupCode,
+ String? groupName,
+ String? cover,
+ bool? have,
+ num? amount,
+ List? emojis,
+}) => SCRoomEmojiRes( id: id ?? _id,
+ sysOrigin: sysOrigin ?? _sysOrigin,
+ groupCode: groupCode ?? _groupCode,
+ groupName: groupName ?? _groupName,
+ cover: cover ?? _cover,
+ have: have ?? _have,
+ amount: amount ?? _amount,
+ emojis: emojis ?? _emojis,
+);
+ String? get id => _id;
+ String? get sysOrigin => _sysOrigin;
+ String? get groupCode => _groupCode;
+ String? get groupName => _groupName;
+ String? get cover => _cover;
+ bool? get have => _have;
+ num? get amount => _amount;
+ List? get emojis => _emojis;
+
+ Map toJson() {
+ final map = {};
+ map['id'] = _id;
+ map['sysOrigin'] = _sysOrigin;
+ map['groupCode'] = _groupCode;
+ map['groupName'] = _groupName;
+ map['cover'] = _cover;
+ map['have'] = _have;
+ map['amount'] = _amount;
+ if (_emojis != null) {
+ map['emojis'] = _emojis?.map((v) => v.toJson()).toList();
+ }
+ return map;
+ }
+
+}
+
+/// sourceUrl : ""
+/// sourceType : 0
+/// coverUrl : ""
+/// type : ""
+
+class Emojis {
+ Emojis({
+ String? sourceUrl,
+ num? sourceType,
+ String? coverUrl,
+ String? type,}){
+ _sourceUrl = sourceUrl;
+ _sourceType = sourceType;
+ _coverUrl = coverUrl;
+ _type = type;
+}
+
+ Emojis.fromJson(dynamic json) {
+ _sourceUrl = json['sourceUrl'];
+ _sourceType = json['sourceType'];
+ _coverUrl = json['coverUrl'];
+ _type = json['type'];
+ }
+ String? _sourceUrl;
+ num? _sourceType;
+ String? _coverUrl;
+ String? _type;
+Emojis copyWith({ String? sourceUrl,
+ num? sourceType,
+ String? coverUrl,
+ String? type,
+}) => Emojis( sourceUrl: sourceUrl ?? _sourceUrl,
+ sourceType: sourceType ?? _sourceType,
+ coverUrl: coverUrl ?? _coverUrl,
+ type: type ?? _type,
+);
+ String? get sourceUrl => _sourceUrl;
+ num? get sourceType => _sourceType;
+ String? get coverUrl => _coverUrl;
+ String? get type => _type;
+
+ Map toJson() {
+ final map = {};
+ map['sourceUrl'] = _sourceUrl;
+ map['sourceType'] = _sourceType;
+ map['coverUrl'] = _coverUrl;
+ map['type'] = _type;
+ return map;
+ }
+
+}
\ No newline at end of file
diff --git a/lib/shared/business_logic/repositories/room_repository.dart b/lib/shared/business_logic/repositories/room_repository.dart
index 841975d..5f61b75 100644
--- a/lib/shared/business_logic/repositories/room_repository.dart
+++ b/lib/shared/business_logic/repositories/room_repository.dart
@@ -131,12 +131,6 @@ abstract class SocialChatRoomRepository {
///麦位表情
Future> emojiAll();
- ///当前用户已拥有的麦位表情
- Future> emojiOwned();
-
- ///购买表情包
- Future purchaseEmoji(String id);
-
///设置房间用户身份
Future changeRoomRole(
String roomId,
diff --git a/lib/shared/data_sources/sources/local/file_cache_manager.dart b/lib/shared/data_sources/sources/local/file_cache_manager.dart
index df0782b..4059012 100644
--- a/lib/shared/data_sources/sources/local/file_cache_manager.dart
+++ b/lib/shared/data_sources/sources/local/file_cache_manager.dart
@@ -1,5 +1,4 @@
import 'dart:io';
-import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
@@ -71,38 +70,20 @@ class MediaCache {
bool ignoreCache = false,
Map? headers,
}) async {
- final stopwatch = Stopwatch()..start();
- _debug('getFile start ignoreCache=$ignoreCache url=$url');
- try {
- if (ignoreCache) {
- // 强制重新下载
- final fileInfo = await _cacheManager.downloadFile(
- url,
- authHeaders: headers,
- );
- _debug(
- 'getFile success bytes=${await fileInfo.file.length()} '
- 'elapsedMs=${stopwatch.elapsedMilliseconds} path=${fileInfo.file.path}',
- );
- return fileInfo.file;
- } else {
- // 优先从缓存获取
- final cacheFile = await _cacheManager.getSingleFile(
- url,
- headers: headers,
- );
- _debug(
- 'getFile success bytes=${await cacheFile.length()} '
- 'elapsedMs=${stopwatch.elapsedMilliseconds} path=${cacheFile.path}',
- );
- return cacheFile;
- }
- } catch (error) {
- _debug(
- 'getFile failed elapsedMs=${stopwatch.elapsedMilliseconds} '
- 'url=$url error=$error',
+ if (ignoreCache) {
+ // 强制重新下载
+ final fileInfo = await _cacheManager.downloadFile(
+ url,
+ authHeaders: headers,
);
- rethrow;
+ return fileInfo.file;
+ } else {
+ // 优先从缓存获取
+ final cacheFile = await _cacheManager.getSingleFile(
+ url,
+ headers: headers,
+ );
+ return cacheFile;
}
}
@@ -153,10 +134,3 @@ class MediaCache {
}
}
}
-
-void _debug(String message) {
- if (!kDebugMode) {
- return;
- }
- debugPrint('[FileCacheManager] $message');
-}
diff --git a/lib/shared/data_sources/sources/local/floating_screen_manager.dart b/lib/shared/data_sources/sources/local/floating_screen_manager.dart
index d663295..c2f36f5 100644
--- a/lib/shared/data_sources/sources/local/floating_screen_manager.dart
+++ b/lib/shared/data_sources/sources/local/floating_screen_manager.dart
@@ -85,23 +85,16 @@ class OverlayManager {
SCFloatingMessage message, {
required String debugLabel,
}) {
- void enqueueNow() {
- if (_isDisposed || _isSuppressed) {
- return;
- }
- unawaited(_warmMessageImages(message));
- _messageQueue.add(message);
- _safeScheduleNext();
- }
-
- if (message.type == 1) {
- enqueueNow();
- return;
- }
-
+ unawaited(_warmMessageImages(message));
SCRoomEffectScheduler().scheduleDeferredEffect(
debugLabel: debugLabel,
- action: enqueueNow,
+ action: () {
+ if (_isDisposed || _isSuppressed) {
+ return;
+ }
+ _messageQueue.add(message);
+ _safeScheduleNext();
+ },
);
}
diff --git a/lib/shared/data_sources/sources/repositories/sc_config_repository_imp.dart b/lib/shared/data_sources/sources/repositories/sc_config_repository_imp.dart
index f3d15b0..c6e9509 100644
--- a/lib/shared/data_sources/sources/repositories/sc_config_repository_imp.dart
+++ b/lib/shared/data_sources/sources/repositories/sc_config_repository_imp.dart
@@ -322,82 +322,37 @@ class SCConfigRepositoryImp implements SocialChatConfigRepository {
///sys/version/manage/release/latest
@override
Future versionManageLatest() async {
- const apiName = 'releaseLatest';
- const path = '/sys/version/manage/release/latest';
- const queryParams = {};
- _debugVersionRequest(apiName, path, queryParams);
- try {
- final result = await http.get(
- "ee9584f714ded864780e47dab2cf4a2e84ac21c90fcd0966a13d2ce9e8845eb8e580afbe66f9f0fef79429cd5c1e0687",
- queryParams: queryParams,
- fromJson: (json) {
- _debugVersionResponse(apiName, path, json);
- return SCVersionManageLatestRes.fromJson(json);
- },
- );
- return result;
- } catch (error) {
- _debugVersionError(apiName, path, queryParams, error);
- rethrow;
- }
+ final result = await http.get(
+ "ee9584f714ded864780e47dab2cf4a2e84ac21c90fcd0966a13d2ce9e8845eb8e580afbe66f9f0fef79429cd5c1e0687",
+ fromJson: (json) {
+ _debugVersionResponse('releaseLatest', json);
+ return SCVersionManageLatestRes.fromJson(json);
+ },
+ );
+ return result;
}
///sys/version/manage/latest/review
@override
Future versionManageLatestReview() async {
- const apiName = 'latestReview';
- const path = '/sys/version/manage/latest/review';
- const queryParams = {};
- _debugVersionRequest(apiName, path, queryParams);
- try {
- final result = await http.get(
- "ee9584f714ded864780e47dab2cf4a2e11ce42bdd061186d4efe3305b73f10fe574aff257ce7e668d08f4caccd1c6232",
- queryParams: queryParams,
- fromJson: (json) {
- _debugVersionResponse(apiName, path, json);
- return VersionManageLatesReviewRes.fromJson(json);
- },
- );
- return result;
- } catch (error) {
- _debugVersionError(apiName, path, queryParams, error);
- rethrow;
- }
- }
-
- void _debugVersionRequest(
- String name,
- String path,
- Map queryParams,
- ) {
- debugPrint(
- '[SCVersionUtils][API] $name request path=$path params=$queryParams',
+ final result = await http.get(
+ "ee9584f714ded864780e47dab2cf4a2e11ce42bdd061186d4efe3305b73f10fe574aff257ce7e668d08f4caccd1c6232",
+ fromJson: (json) {
+ _debugVersionResponse('latestReview', json);
+ return VersionManageLatesReviewRes.fromJson(json);
+ },
);
+ return result;
}
- void _debugVersionResponse(String name, String path, dynamic json) {
+ void _debugVersionResponse(String name, dynamic json) {
try {
- debugPrint(
- '[SCVersionUtils][API] $name response path=$path rawBody=${jsonEncode(json)}',
- );
+ debugPrint('[SCVersionUtils] $name rawBody=${jsonEncode(json)}');
} catch (e) {
- debugPrint(
- '[SCVersionUtils][API] $name response path=$path rawBody=$json encodeError=$e',
- );
+ debugPrint('[SCVersionUtils] $name rawBody=$json encodeError=$e');
}
}
- void _debugVersionError(
- String name,
- String path,
- Map queryParams,
- Object error,
- ) {
- debugPrint(
- '[SCVersionUtils][API] $name failed path=$path params=$queryParams error=$error',
- );
- }
-
///sys/config/customer-service
@override
Future customerService() async {
diff --git a/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart b/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart
index 3db8554..602b4bb 100644
--- a/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart
+++ b/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart
@@ -55,45 +55,6 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
debugPrint('[GiftFx][repo] $message');
}
- void _roomEmojiRepoDebugLog(String message) {
- debugPrint('[RoomEmoji][repo] $message');
- }
-
- void _roomEmojiRequestLog(
- String method,
- String path, {
- dynamic query,
- dynamic data,
- }) {
- _roomEmojiRepoDebugLog(
- 'request method=$method path=$path '
- 'query=${_roomEmojiDebugJson(query ?? const {})} '
- 'body=${_roomEmojiDebugJson(data)}',
- );
- }
-
- void _roomEmojiResponseLog(String method, String path, dynamic responseBody) {
- _roomEmojiRepoDebugLog(
- 'response method=$method path=$path '
- 'responseBody(unwrapped)=${_roomEmojiDebugJson(responseBody)}',
- );
- }
-
- String _roomEmojiDebugJson(dynamic data) {
- return _clipGiftDebug(_stringifyGiftDebug(data));
- }
-
- int _roomEmojiTotal(List groups) {
- return groups.fold(
- 0,
- (total, group) => total + (group.emojis?.length ?? 0),
- );
- }
-
- int _ownedEmojiGroups(List groups) {
- return groups.where((group) => group.have == true).length;
- }
-
String _giftRepoErrorDetails(Object error) {
if (error is DioException) {
final request = error.requestOptions;
@@ -807,87 +768,13 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
///material/emoji/all
@override
Future> emojiAll() async {
- const method = 'GET';
- const path = '/material/emoji/all';
- _roomEmojiRequestLog(method, path);
- try {
- final result = await http.get>(
- "1d2546ea50639e65f04d029dd0a9655eda4aeb3a4bd182b663d60d87cb3f2938",
- extra: const {BaseNetworkClient.silentErrorToastKey: true},
- fromJson: (json) {
- _roomEmojiResponseLog(method, path, json);
- final list = json is List ? json : const [];
- _roomEmojiRepoDebugLog(
- 'parse method=$method path=$path bodyType=${json.runtimeType} '
- 'groups=${list.length}',
- );
- return list.map((e) => SCRoomEmojiRes.fromJson(e)).toList();
- },
- );
- _roomEmojiRepoDebugLog(
- 'success method=$method path=$path groups=${result.length} '
- 'emojiTotal=${_roomEmojiTotal(result)} ownedGroups=${_ownedEmojiGroups(result)}',
- );
- return result;
- } catch (error) {
- _roomEmojiRepoDebugLog(
- 'failed method=$method path=$path error=${_giftRepoErrorDetails(error)}',
- );
- rethrow;
- }
- }
-
- ///material/emoji
- @override
- Future> emojiOwned() async {
- const method = 'GET';
- const path = '/material/emoji';
- _roomEmojiRequestLog(method, path);
- try {
- final result = await http.get>(
- path,
- extra: const {BaseNetworkClient.silentErrorToastKey: true},
- fromJson: (json) {
- _roomEmojiResponseLog(method, path, json);
- final list = json is List ? json : const [];
- _roomEmojiRepoDebugLog(
- 'parse method=$method path=$path bodyType=${json.runtimeType} '
- 'groups=${list.length}',
- );
- return list.map((e) => SCRoomEmojiRes.fromJson(e)).toList();
- },
- );
- _roomEmojiRepoDebugLog(
- 'success method=$method path=$path groups=${result.length} '
- 'emojiTotal=${_roomEmojiTotal(result)}',
- );
- return result;
- } catch (error) {
- _roomEmojiRepoDebugLog(
- 'failed method=$method path=$path error=${_giftRepoErrorDetails(error)}',
- );
- rethrow;
- }
- }
-
- ///material/emoji/purchase
- @override
- Future purchaseEmoji(String id) async {
- const method = 'POST';
- const path = '/material/emoji/purchase';
- final params = {"id": id};
- _roomEmojiRequestLog(method, path, data: params);
- await http.post