diff --git a/Explorer/Assets/AddressableAssetsData/AssetGroups/Essentials.asset b/Explorer/Assets/AddressableAssetsData/AssetGroups/Essentials.asset index 896a8104c4..6fd2dfe4b5 100644 --- a/Explorer/Assets/AddressableAssetsData/AssetGroups/Essentials.asset +++ b/Explorer/Assets/AddressableAssetsData/AssetGroups/Essentials.asset @@ -22,6 +22,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: 05e6ec17311e94dfbb4c3e08aaea0891 + m_Address: FriendsPanel + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 0703f7736b8c9b244953ae7c6ff9a037 m_Address: LODSettings.asset m_ReadOnly: 0 @@ -342,6 +347,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: a341d15dd51544eb9985664704a7c43a + m_Address: Assets/DCL/Friends/UI/Prefabs/UnfriendConfirmationPopup.prefab + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: a39f0a305be6be143aabcbf7ebb59d62 m_Address: RoomIndicator m_ReadOnly: 0 diff --git a/Explorer/Assets/DCL/Audio/AudioConfigs/FriendRequest_Confirm_Cheer.asset b/Explorer/Assets/DCL/Audio/AudioConfigs/FriendRequest_Confirm_Cheer.asset new file mode 100644 index 0000000000..a98a88ec96 --- /dev/null +++ b/Explorer/Assets/DCL/Audio/AudioConfigs/FriendRequest_Confirm_Cheer.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: FriendRequest_Confirm_Cheer + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: 6aabe45353eca774e825c2f529aa7ec7, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Audio/AudioConfigs/FriendRequest_Confirm_Cheer.asset.meta b/Explorer/Assets/DCL/Audio/AudioConfigs/FriendRequest_Confirm_Cheer.asset.meta new file mode 100644 index 0000000000..c7f97c2644 --- /dev/null +++ b/Explorer/Assets/DCL/Audio/AudioConfigs/FriendRequest_Confirm_Cheer.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 668410f366ba7433fa951a769a903a0d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Browser/DecentralandUrls/DecentralandUrlsSource.cs b/Explorer/Assets/DCL/Browser/DecentralandUrls/DecentralandUrlsSource.cs index 415c8696f2..26a6e19628 100644 --- a/Explorer/Assets/DCL/Browser/DecentralandUrls/DecentralandUrlsSource.cs +++ b/Explorer/Assets/DCL/Browser/DecentralandUrls/DecentralandUrlsSource.cs @@ -106,9 +106,7 @@ private static string RawUrl(DecentralandUrl decentralandUrl) => DecentralandUrl.CameraReelImages => $"https://camera-reel-service.decentraland.{ENV}/api/images", DecentralandUrl.CameraReelPlaces => $"https://camera-reel-service.decentraland.{ENV}/api/places", DecentralandUrl.CameraReelLink => $"https://reels.decentraland.{ENV}", - // TODO: use the environment once the service is deployed to prod - DecentralandUrl.ApiFriends => "wss://rpc-social-service-ea.decentraland.zone", - // DecentralandUrl.ApiFriends => $"wss://rpc-social-service-ea.decentraland.{ENV}", + DecentralandUrl.ApiFriends => $"wss://rpc-social-service-ea.decentraland.{ENV}", DecentralandUrl.AssetBundleRegistry => $"https://asset-bundle-registry.decentraland.{ENV}/entities/active", _ => throw new ArgumentOutOfRangeException(nameof(decentralandUrl), decentralandUrl, null!) }; diff --git a/Explorer/Assets/DCL/Chat/Chat.asmdef b/Explorer/Assets/DCL/Chat/Chat.asmdef index 8b16b778d9..41f6294dde 100644 --- a/Explorer/Assets/DCL/Chat/Chat.asmdef +++ b/Explorer/Assets/DCL/Chat/Chat.asmdef @@ -30,7 +30,8 @@ "GUID:875a5d5129614170bd769ed012c2eb3d", "GUID:f4a0f40a2545482b8929d4c3c642f50a", "GUID:ca4e81cdd6a34d1aa54c32ad41fc5b3b", - "GUID:54660b0fae444b4cbfdafa9d68108f04" + "GUID:54660b0fae444b4cbfdafa9d68108f04", + "GUID:977f79b5e6de8468d8967dfff5ca8128" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Explorer/Assets/DCL/Chat/ChatController.cs b/Explorer/Assets/DCL/Chat/ChatController.cs index 9d45f51089..5e3fce454a 100644 --- a/Explorer/Assets/DCL/Chat/ChatController.cs +++ b/Explorer/Assets/DCL/Chat/ChatController.cs @@ -6,6 +6,7 @@ using DCL.Chat.History; using DCL.Chat.MessageBus; using DCL.Emoji; +using DCL.Friends.Chat; using DCL.Input; using DCL.Input.Component; using DCL.Input.Systems; @@ -83,7 +84,8 @@ public ChatController( Entity playerEntity, DCLInput dclInput, IEventSystem eventSystem, - IInputBlock inputBlock + IInputBlock inputBlock, + IChatLifecycleBusController chatLifecycleBusController ) : base(viewFactory) { this.chatEntryConfiguration = chatEntryConfiguration; @@ -102,7 +104,15 @@ IInputBlock inputBlock this.eventSystem = eventSystem; this.inputBlock = inputBlock; + chatLifecycleBusController.SubscribeToHideChatCommand(HideBusCommandReceived); + device = InputSystem.GetDevice(); + + } + + private void HideBusCommandReceived() + { + HideViewAsync(CancellationToken.None).Forget(); } protected override void OnViewInstantiated() @@ -147,6 +157,9 @@ protected override void OnViewShow() dclInput.Shortcuts.ToggleNametags.performed += ToggleNametagsFromShortcut; dclInput.Shortcuts.OpenChat.performed += OnOpenChat; dclInput.Shortcuts.OpenChatCommandLine.performed += OnOpenChatCommand; + + viewInstance!.LoopList.RefreshAllShownItem(); + OnFocus(); } protected override void OnViewClose() @@ -156,6 +169,7 @@ protected override void OnViewClose() dclInput.Shortcuts.ToggleNametags.performed -= ToggleNametagsFromShortcut; dclInput.Shortcuts.OpenChat.performed -= OnOpenChat; dclInput.Shortcuts.OpenChatCommandLine.performed -= OnOpenChatCommand; + OnBlur(); } private void OnClick(InputAction.CallbackContext obj) diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton.meta b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton.meta new file mode 100644 index 0000000000..5977067cb0 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d5ee448db9543475dbdba067a7c2f14c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Active.anim b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Active.anim new file mode 100644 index 0000000000..4c3bc3387b --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Active.anim @@ -0,0 +1,359 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Active + serializedVersion: 7 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: Background + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 16 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 16 + m_PPtrCurves: [] + m_SampleRate: 60 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 4080383872 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3146825030 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 2734955949 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3779823699 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 847614765 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + pptrCurveMapping: [] + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 0 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: Background + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 16 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 16 + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Active.anim.meta b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Active.anim.meta new file mode 100644 index 0000000000..953fa6d4c4 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Active.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 27ffcc76185364ec9941969aef798fa1 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Empty.anim b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Empty.anim new file mode 100644 index 0000000000..65df7bc9ee --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Empty.anim @@ -0,0 +1,359 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Empty + serializedVersion: 7 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: Background + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 16 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 16 + m_PPtrCurves: [] + m_SampleRate: 60 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 4080383872 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3146825030 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 2734955949 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3779823699 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 847614765 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + pptrCurveMapping: [] + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 0 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: Background + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 16 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 16 + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Empty.anim.meta b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Empty.anim.meta new file mode 100644 index 0000000000..b350e5571e --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Empty.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 75adc88130272413ca452869be1a3acb +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/FriendsButtonOverrideAnimatorController.overrideController b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/FriendsButtonOverrideAnimatorController.overrideController new file mode 100644 index 0000000000..36eca8e0e2 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/FriendsButtonOverrideAnimatorController.overrideController @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!221 &22100000 +AnimatorOverrideController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FriendsButtonOverrideAnimatorController + m_Controller: {fileID: 9100000, guid: 29515e57a75974c4480e658f7421d023, type: 2} + m_Clips: + - m_OriginalClip: {fileID: 7400000, guid: 43e9f4fd4bce23e46b692fbe72981b78, type: 2} + m_OverrideClip: {fileID: 7400000, guid: 75adc88130272413ca452869be1a3acb, type: 2} + - m_OriginalClip: {fileID: 7400000, guid: ea61896d50f57c949b869912b2e72b67, type: 2} + m_OverrideClip: {fileID: 7400000, guid: 05e1b364963e440a7bb84eb5e84d25c8, type: 2} + - m_OriginalClip: {fileID: 7400000, guid: 3935cf9ff713f5e4badf081a136f96bb, type: 2} + m_OverrideClip: {fileID: 7400000, guid: 803862cd9bb2844d19ce54164cc33a4d, type: 2} + - m_OriginalClip: {fileID: 7400000, guid: b0a045939fb100b438fdb51c4b59789e, type: 2} + m_OverrideClip: {fileID: 7400000, guid: 27ffcc76185364ec9941969aef798fa1, type: 2} diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/FriendsButtonOverrideAnimatorController.overrideController.meta b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/FriendsButtonOverrideAnimatorController.overrideController.meta new file mode 100644 index 0000000000..74ca05c8dc --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/FriendsButtonOverrideAnimatorController.overrideController.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db4dd4a74ff3544c686ad48bdb223343 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 22100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Hover.anim b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Hover.anim new file mode 100644 index 0000000000..15f5e22cfe --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Hover.anim @@ -0,0 +1,912 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Hover + serializedVersion: 7 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: HoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.083333336 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.r + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.g + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.b + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.a + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_hasFontAssetChanged + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnhoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + m_PPtrCurves: + - serializedVersion: 2 + curve: + - time: 0 + value: {fileID: 21300000, guid: e55e467aa1f0c436483056cd26064aab, type: 3} + - time: 0.016666668 + value: {fileID: 21300000, guid: ba181ab49075b47e28087f67b83a03e7, type: 3} + - time: 0.033333335 + value: {fileID: 21300000, guid: f987a4a54bfaa4d63b8af830a61f4317, type: 3} + - time: 0.05 + value: {fileID: 21300000, guid: 2d04a5f4feb9942c291ba173a54ae239, type: 3} + - time: 0.06666667 + value: {fileID: 21300000, guid: 83cf2289b3b0e483794ba0534c285d5b, type: 3} + - time: 0.083333336 + value: {fileID: 21300000, guid: 61c765264280f462d9996087cdda83db, type: 3} + - time: 0.1 + value: {fileID: 21300000, guid: 9e59fbec643814b06a6869404663ee81, type: 3} + - time: 0.11666667 + value: {fileID: 21300000, guid: 3c41d1a36121c4b0ea158be10d6bb01f, type: 3} + - time: 0.13333334 + value: {fileID: 21300000, guid: c5077e8054e8742f8b2f9a5b114965dc, type: 3} + - time: 0.15 + value: {fileID: 21300000, guid: e5d53705eb8ea4e34b28e0da2387be78, type: 3} + - time: 0.16666667 + value: {fileID: 21300000, guid: eb6579ef8a36d40ada301ded112665de, type: 3} + - time: 0.18333334 + value: {fileID: 21300000, guid: f8bcf1d019cb3468eaf6347338b4a032, type: 3} + - time: 0.2 + value: {fileID: 21300000, guid: e3cedb3e64da740d58d6d4fb1e302bdd, type: 3} + - time: 0.21666667 + value: {fileID: 21300000, guid: b162d94d0846c4492b37e3f9a94ff486, type: 3} + - time: 0.23333333 + value: {fileID: 21300000, guid: d5a6dd1f4f90b4b338981047dee8ee65, type: 3} + - time: 0.25 + value: {fileID: 21300000, guid: f3390ec7992524b6db0a48eac26f1585, type: 3} + - time: 0.26666668 + value: {fileID: 21300000, guid: 760431222aff241838eb6c5e03c30a01, type: 3} + - time: 0.28333333 + value: {fileID: 21300000, guid: 5e6bf000b738949d8bf26fc81846b8e6, type: 3} + - time: 0.3 + value: {fileID: 21300000, guid: 340c752549cdd47aebad1d2f75ee1cf1, type: 3} + - time: 0.31666666 + value: {fileID: 21300000, guid: 01f2de33951eb4482be02602bcad898f, type: 3} + - time: 0.33333334 + value: {fileID: 21300000, guid: 96798bd9690d04bf683da839cf17a9b3, type: 3} + - time: 0.35 + value: {fileID: 21300000, guid: bc402e19848234ada923563589b12a4f, type: 3} + - time: 0.36666667 + value: {fileID: 21300000, guid: 90517152b639c41e7a8ff521176a0f55, type: 3} + - time: 0.38333333 + value: {fileID: 21300000, guid: 7479bd9b00d804666bb2e41da95ff2d3, type: 3} + - time: 0.4 + value: {fileID: 21300000, guid: 6fb8d0bd32f3d44ab83c4be4cb8a4ea3, type: 3} + - time: 0.41666666 + value: {fileID: 21300000, guid: 13b6ad192f4bd4c11a1302f02b3e20d4, type: 3} + - time: 0.43333334 + value: {fileID: 21300000, guid: c8adbf654929a4048b22f5532810c5d1, type: 3} + - time: 0.45 + value: {fileID: 21300000, guid: d85e26137c6d94d70985673be2b7beb3, type: 3} + - time: 0.46666667 + value: {fileID: 21300000, guid: a4456b2c212e743fa8c2a3873cb6630d, type: 3} + - time: 0.48333332 + value: {fileID: 21300000, guid: 73b0c3da466e64ef6b0613dc40131229, type: 3} + - time: 0.5 + value: {fileID: 21300000, guid: 3437ad4c8b668469f82864fec70b0e34, type: 3} + - time: 0.51666665 + value: {fileID: 21300000, guid: f5b8e410e8e9e469991b819590c1799d, type: 3} + - time: 0.53333336 + value: {fileID: 21300000, guid: caa1b51f61ec04491a81fe89e1cb434f, type: 3} + - time: 0.55 + value: {fileID: 21300000, guid: d186c9e3bd47c4b74b87abfb7e557a27, type: 3} + - time: 0.56666666 + value: {fileID: 21300000, guid: 4b5b9ceaa8b894fb6a10416fbf5e8956, type: 3} + - time: 0.5833333 + value: {fileID: 21300000, guid: 0c5b8348b668942beb43f3c5f0e62e50, type: 3} + - time: 0.6 + value: {fileID: 21300000, guid: 565cf059f05fc471eadf232c79f6eb32, type: 3} + - time: 0.6166667 + value: {fileID: 21300000, guid: ff7cd7bedae88431c857f4d280585e49, type: 3} + - time: 0.6333333 + value: {fileID: 21300000, guid: 57fa32d5971dc496fadd7d98244a5a18, type: 3} + - time: 0.65 + value: {fileID: 21300000, guid: 1eddb387d6b46454bbf17cc36d8d9f45, type: 3} + - time: 0.6666667 + value: {fileID: 21300000, guid: d3bbf2f151038454f97aca48dc9607b0, type: 3} + - time: 0.68333334 + value: {fileID: 21300000, guid: d0e07d6e0bff84670adad43142b3abb9, type: 3} + - time: 0.7 + value: {fileID: 21300000, guid: b4a3cebc28e6243aa92b0a76b99962e1, type: 3} + - time: 0.71666664 + value: {fileID: 21300000, guid: 3f7737f08261f4b87bfe427bfe631bb5, type: 3} + - time: 0.73333335 + value: {fileID: 21300000, guid: af6c7c6f15099489ea85de137360186f, type: 3} + - time: 0.75 + value: {fileID: 21300000, guid: 957e744874bf84755bb8b681e8bdc160, type: 3} + - time: 0.76666665 + value: {fileID: 21300000, guid: ce5964ff27e65432d83b4159f822fe9c, type: 3} + attribute: m_Sprite + path: HoverSprites + classID: 114 + script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + flags: 2 + m_SampleRate: 60 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 847614765 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3541911651 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3779823699 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 4185109675 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 908169384 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 922705709 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 2734955949 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3146825030 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3541911651 + attribute: 2015549526 + script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 1 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 2110649717 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 269488542 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 1618666769 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + pptrCurveMapping: + - {fileID: 21300000, guid: e55e467aa1f0c436483056cd26064aab, type: 3} + - {fileID: 21300000, guid: ba181ab49075b47e28087f67b83a03e7, type: 3} + - {fileID: 21300000, guid: f987a4a54bfaa4d63b8af830a61f4317, type: 3} + - {fileID: 21300000, guid: 2d04a5f4feb9942c291ba173a54ae239, type: 3} + - {fileID: 21300000, guid: 83cf2289b3b0e483794ba0534c285d5b, type: 3} + - {fileID: 21300000, guid: 61c765264280f462d9996087cdda83db, type: 3} + - {fileID: 21300000, guid: 9e59fbec643814b06a6869404663ee81, type: 3} + - {fileID: 21300000, guid: 3c41d1a36121c4b0ea158be10d6bb01f, type: 3} + - {fileID: 21300000, guid: c5077e8054e8742f8b2f9a5b114965dc, type: 3} + - {fileID: 21300000, guid: e5d53705eb8ea4e34b28e0da2387be78, type: 3} + - {fileID: 21300000, guid: eb6579ef8a36d40ada301ded112665de, type: 3} + - {fileID: 21300000, guid: f8bcf1d019cb3468eaf6347338b4a032, type: 3} + - {fileID: 21300000, guid: e3cedb3e64da740d58d6d4fb1e302bdd, type: 3} + - {fileID: 21300000, guid: b162d94d0846c4492b37e3f9a94ff486, type: 3} + - {fileID: 21300000, guid: d5a6dd1f4f90b4b338981047dee8ee65, type: 3} + - {fileID: 21300000, guid: f3390ec7992524b6db0a48eac26f1585, type: 3} + - {fileID: 21300000, guid: 760431222aff241838eb6c5e03c30a01, type: 3} + - {fileID: 21300000, guid: 5e6bf000b738949d8bf26fc81846b8e6, type: 3} + - {fileID: 21300000, guid: 340c752549cdd47aebad1d2f75ee1cf1, type: 3} + - {fileID: 21300000, guid: 01f2de33951eb4482be02602bcad898f, type: 3} + - {fileID: 21300000, guid: 96798bd9690d04bf683da839cf17a9b3, type: 3} + - {fileID: 21300000, guid: bc402e19848234ada923563589b12a4f, type: 3} + - {fileID: 21300000, guid: 90517152b639c41e7a8ff521176a0f55, type: 3} + - {fileID: 21300000, guid: 7479bd9b00d804666bb2e41da95ff2d3, type: 3} + - {fileID: 21300000, guid: 6fb8d0bd32f3d44ab83c4be4cb8a4ea3, type: 3} + - {fileID: 21300000, guid: 13b6ad192f4bd4c11a1302f02b3e20d4, type: 3} + - {fileID: 21300000, guid: c8adbf654929a4048b22f5532810c5d1, type: 3} + - {fileID: 21300000, guid: d85e26137c6d94d70985673be2b7beb3, type: 3} + - {fileID: 21300000, guid: a4456b2c212e743fa8c2a3873cb6630d, type: 3} + - {fileID: 21300000, guid: 73b0c3da466e64ef6b0613dc40131229, type: 3} + - {fileID: 21300000, guid: 3437ad4c8b668469f82864fec70b0e34, type: 3} + - {fileID: 21300000, guid: f5b8e410e8e9e469991b819590c1799d, type: 3} + - {fileID: 21300000, guid: caa1b51f61ec04491a81fe89e1cb434f, type: 3} + - {fileID: 21300000, guid: d186c9e3bd47c4b74b87abfb7e557a27, type: 3} + - {fileID: 21300000, guid: 4b5b9ceaa8b894fb6a10416fbf5e8956, type: 3} + - {fileID: 21300000, guid: 0c5b8348b668942beb43f3c5f0e62e50, type: 3} + - {fileID: 21300000, guid: 565cf059f05fc471eadf232c79f6eb32, type: 3} + - {fileID: 21300000, guid: ff7cd7bedae88431c857f4d280585e49, type: 3} + - {fileID: 21300000, guid: 57fa32d5971dc496fadd7d98244a5a18, type: 3} + - {fileID: 21300000, guid: 1eddb387d6b46454bbf17cc36d8d9f45, type: 3} + - {fileID: 21300000, guid: d3bbf2f151038454f97aca48dc9607b0, type: 3} + - {fileID: 21300000, guid: d0e07d6e0bff84670adad43142b3abb9, type: 3} + - {fileID: 21300000, guid: b4a3cebc28e6243aa92b0a76b99962e1, type: 3} + - {fileID: 21300000, guid: 3f7737f08261f4b87bfe427bfe631bb5, type: 3} + - {fileID: 21300000, guid: af6c7c6f15099489ea85de137360186f, type: 3} + - {fileID: 21300000, guid: 957e744874bf84755bb8b681e8bdc160, type: 3} + - {fileID: 21300000, guid: ce5964ff27e65432d83b4159f822fe9c, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.7833333 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 0 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: HoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.083333336 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.r + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.g + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.b + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.a + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_hasFontAssetChanged + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnhoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Hover.anim.meta b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Hover.anim.meta new file mode 100644 index 0000000000..d1750b47e8 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Hover.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 05e1b364963e440a7bb84eb5e84d25c8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Unhover.anim b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Unhover.anim new file mode 100644 index 0000000000..d056cd13e8 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Unhover.anim @@ -0,0 +1,814 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Unhover + serializedVersion: 7 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.016666668 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: HoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.016666668 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.1 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnhoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.1 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.r + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.g + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.b + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.a + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + m_PPtrCurves: + - serializedVersion: 2 + curve: + - time: 0 + value: {fileID: 21300000, guid: af5f709eaf2604e2fa417448d115ff21, type: 3} + attribute: m_Sprite + path: HoverSprites + classID: 114 + script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + flags: 2 + - serializedVersion: 2 + curve: + - time: 0.016666668 + value: {fileID: 21300000, guid: af5f709eaf2604e2fa417448d115ff21, type: 3} + - time: 0.033333335 + value: {fileID: 21300000, guid: de858e1549ca24887858528d93f2b28f, type: 3} + - time: 0.05 + value: {fileID: 21300000, guid: 1b6764a2420864b99a2f3d727e45a85a, type: 3} + - time: 0.06666667 + value: {fileID: 21300000, guid: 42d71e6e57ae44817a0e52fe839b920b, type: 3} + - time: 0.083333336 + value: {fileID: 21300000, guid: 1d2bc1ae05357432abeebae9cc820c20, type: 3} + - time: 0.1 + value: {fileID: 21300000, guid: b320482e7610343adbb91f139d3ec8d6, type: 3} + - time: 0.11666667 + value: {fileID: 21300000, guid: 77263f2d1113e4e6bb1b9e91f72726c9, type: 3} + - time: 0.13333334 + value: {fileID: 21300000, guid: 0963bc7e1653b4b7794eaca40cac46e9, type: 3} + - time: 0.15 + value: {fileID: 21300000, guid: bb2a2c6e14fa04d1abefbfc249bef908, type: 3} + - time: 0.16666667 + value: {fileID: 21300000, guid: d2f2c38af26ce4e6e9dfe743f153628c, type: 3} + - time: 0.18333334 + value: {fileID: 21300000, guid: bade29ccaffe74dabbce77f0dffce2be, type: 3} + - time: 0.2 + value: {fileID: 21300000, guid: fd1b3923552f94580860374f20d6ede3, type: 3} + - time: 0.21666667 + value: {fileID: 21300000, guid: 62e31b31351e24a93b034327543f303d, type: 3} + - time: 0.23333333 + value: {fileID: 21300000, guid: ed2b4f2cbce514e7e9e1255bef305af8, type: 3} + - time: 0.25 + value: {fileID: 21300000, guid: 3a394c3e53d644026880b3fa8565932e, type: 3} + - time: 0.26666668 + value: {fileID: 21300000, guid: 797e9514e84824880bbb7611284161cd, type: 3} + - time: 0.28333333 + value: {fileID: 21300000, guid: 2c7e3969488f9404daff24805c1eda16, type: 3} + - time: 0.3 + value: {fileID: 21300000, guid: 679d0d15f198c4a30b30cd2a074d4614, type: 3} + attribute: m_Sprite + path: UnhoverSprites + classID: 114 + script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + flags: 2 + m_SampleRate: 60 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 3541911651 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 922705709 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 847614765 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 4185109675 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3779823699 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 2734955949 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3146825030 + attribute: 2086281974 + script: {fileID: 0} + typeID: 1 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 3541911651 + attribute: 2015549526 + script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 1 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 922705709 + attribute: 2015549526 + script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 1 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 2110649717 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 269488542 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + - serializedVersion: 2 + path: 78225181 + attribute: 1618666769 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + typeID: 114 + customType: 0 + isPPtrCurve: 0 + isIntCurve: 0 + isSerializeReferenceCurve: 0 + pptrCurveMapping: + - {fileID: 21300000, guid: af5f709eaf2604e2fa417448d115ff21, type: 3} + - {fileID: 21300000, guid: af5f709eaf2604e2fa417448d115ff21, type: 3} + - {fileID: 21300000, guid: de858e1549ca24887858528d93f2b28f, type: 3} + - {fileID: 21300000, guid: 1b6764a2420864b99a2f3d727e45a85a, type: 3} + - {fileID: 21300000, guid: 42d71e6e57ae44817a0e52fe839b920b, type: 3} + - {fileID: 21300000, guid: 1d2bc1ae05357432abeebae9cc820c20, type: 3} + - {fileID: 21300000, guid: b320482e7610343adbb91f139d3ec8d6, type: 3} + - {fileID: 21300000, guid: 77263f2d1113e4e6bb1b9e91f72726c9, type: 3} + - {fileID: 21300000, guid: 0963bc7e1653b4b7794eaca40cac46e9, type: 3} + - {fileID: 21300000, guid: bb2a2c6e14fa04d1abefbfc249bef908, type: 3} + - {fileID: 21300000, guid: d2f2c38af26ce4e6e9dfe743f153628c, type: 3} + - {fileID: 21300000, guid: bade29ccaffe74dabbce77f0dffce2be, type: 3} + - {fileID: 21300000, guid: fd1b3923552f94580860374f20d6ede3, type: 3} + - {fileID: 21300000, guid: 62e31b31351e24a93b034327543f303d, type: 3} + - {fileID: 21300000, guid: ed2b4f2cbce514e7e9e1255bef305af8, type: 3} + - {fileID: 21300000, guid: 3a394c3e53d644026880b3fa8565932e, type: 3} + - {fileID: 21300000, guid: 797e9514e84824880bbb7611284161cd, type: 3} + - {fileID: 21300000, guid: 2c7e3969488f9404daff24805c1eda16, type: 3} + - {fileID: 21300000, guid: 679d0d15f198c4a30b30cd2a074d4614, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.3166667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 0 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.016666668 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: HoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.016666668 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.1 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnhoverSprites + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.1 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.r + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.g + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.b + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 0.083333336 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 136 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_fontColor.a + path: SelectedText + classID: 114 + script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 0.083333336 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: UnselectedText + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedImage + classID: 1 + script: {fileID: 0} + flags: 0 + - serializedVersion: 2 + curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: Infinity + outSlope: Infinity + tangentMode: 103 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + attribute: m_IsActive + path: SelectedBackground + classID: 1 + script: {fileID: 0} + flags: 0 + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Unhover.anim.meta b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Unhover.anim.meta new file mode 100644 index 0000000000..4d7fadb2f8 --- /dev/null +++ b/Explorer/Assets/DCL/ExplorePanel/Assets/Animations/FriendsButton/Unhover.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 803862cd9bb2844d19ce54164cc33a4d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/FeatureFlags/FeatureFlagsStrings.cs b/Explorer/Assets/DCL/FeatureFlags/FeatureFlagsStrings.cs index c5f8dd1ed4..aa9da172d9 100644 --- a/Explorer/Assets/DCL/FeatureFlags/FeatureFlagsStrings.cs +++ b/Explorer/Assets/DCL/FeatureFlags/FeatureFlagsStrings.cs @@ -23,5 +23,8 @@ public static class FeatureFlagsStrings public const string ASSET_BUNDLE_FALLBACK = "alfa-asset-bundle-fallback"; public const string CAMERA_REEL = "alfa-camera-reel"; + public const string FRIENDS = "alfa-friends"; + public const string FRIENDS_USER_BLOCKING = "alfa-friends-user-blocking"; + public const string FRIENDS_ONLINE_STATUS = "alfa-friends-online-status"; } } diff --git a/Explorer/Assets/DCL/Friends/Chat.meta b/Explorer/Assets/DCL/Friends/Chat.meta new file mode 100644 index 0000000000..79ee777577 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Chat.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2e334ebffed844c0b3a20716134c972b +timeCreated: 1738579220 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/Chat/DCL.Friends.Chat.asmdef b/Explorer/Assets/DCL/Friends/Chat/DCL.Friends.Chat.asmdef new file mode 100644 index 0000000000..9bc73c747c --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Chat/DCL.Friends.Chat.asmdef @@ -0,0 +1,14 @@ +{ + "name": "DCL.Friends.Chat", + "rootNamespace": "", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/Chat/DCL.Friends.Chat.asmdef.meta b/Explorer/Assets/DCL/Friends/Chat/DCL.Friends.Chat.asmdef.meta new file mode 100644 index 0000000000..cfbb03c74b --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Chat/DCL.Friends.Chat.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 977f79b5e6de8468d8967dfff5ca8128 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/Chat/IChatLifecycleBusController.cs b/Explorer/Assets/DCL/Friends/Chat/IChatLifecycleBusController.cs new file mode 100644 index 0000000000..c81042a4a3 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Chat/IChatLifecycleBusController.cs @@ -0,0 +1,12 @@ +using System; + +namespace DCL.Friends.Chat +{ + public interface IChatLifecycleBusController + { + void ShowChat(); + void HideChat(); + + void SubscribeToHideChatCommand(Action action); + } +} diff --git a/Explorer/Assets/DCL/Friends/Chat/IChatLifecycleBusController.cs.meta b/Explorer/Assets/DCL/Friends/Chat/IChatLifecycleBusController.cs.meta new file mode 100644 index 0000000000..8359b658af --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Chat/IChatLifecycleBusController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cccf06d8769d4a9a916e958708fc171c +timeCreated: 1738579385 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/ChatLifecycleBusController.cs b/Explorer/Assets/DCL/Friends/ChatLifecycleBusController.cs new file mode 100644 index 0000000000..b3e4dd96ea --- /dev/null +++ b/Explorer/Assets/DCL/Friends/ChatLifecycleBusController.cs @@ -0,0 +1,32 @@ +using Cysharp.Threading.Tasks; +using DCL.Chat; +using DCL.Friends.Chat; +using MVC; +using System; + +namespace DCL.Friends +{ + public class ChatLifecycleBusController : IChatLifecycleBusController, IDisposable + { + private readonly IMVCManager mvcManager; + + private event Action? ChatHideAction; + + public ChatLifecycleBusController(IMVCManager mvcManager) + { + this.mvcManager = mvcManager; + } + + public void ShowChat() => + mvcManager.ShowAsync(ChatController.IssueCommand()).Forget(); + + public void HideChat() => + ChatHideAction?.Invoke(); + + public void SubscribeToHideChatCommand(Action action) => + ChatHideAction += action; + + public void Dispose() => + ChatHideAction = null; + } +} diff --git a/Explorer/Assets/DCL/Friends/ChatLifecycleBusController.cs.meta b/Explorer/Assets/DCL/Friends/ChatLifecycleBusController.cs.meta new file mode 100644 index 0000000000..131f87b315 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/ChatLifecycleBusController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 04806ae767ef4d67a982479fd9b72cef +timeCreated: 1738579545 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/DCL.Friends.asmdef b/Explorer/Assets/DCL/Friends/DCL.Friends.asmdef index d5896b61c4..0d0d6911bf 100644 --- a/Explorer/Assets/DCL/Friends/DCL.Friends.asmdef +++ b/Explorer/Assets/DCL/Friends/DCL.Friends.asmdef @@ -11,15 +11,30 @@ "GUID:5ab29fa8ae5769b49ab29e390caca7a4", "GUID:ca4e81cdd6a34d1aa54c32ad41fc5b3b", "GUID:3640f3c0b42946b0b8794a1ed8e06ca5", - "GUID:5462d37de7d4344df8aade58a45b403e", - "GUID:766b242fb43af451aaa331f39872177d", - "GUID:b97826ed91484ac1af953a8afc03316e", "GUID:45f6fff651a0a514f8edfdaf9cce45af", - "GUID:e169fa6683c924c7e99a85981a91d953", + "GUID:b97826ed91484ac1af953a8afc03316e", + "GUID:ba053ae967dabc94a811350e36a486f3", "GUID:6055be8ebefd69e48b49212b09b47b2f", + "GUID:5462d37de7d4344df8aade58a45b403e", + "GUID:e169fa6683c924c7e99a85981a91d953", + "GUID:2f30d6e5229a74284acedda491abcc6e", "GUID:4a12c0b1b77ec6b418a8d7bd5c925be3", + "GUID:166b65e6dfc848bb9fb075f53c293a38", "GUID:4794e238ed0f65142a4aea5848b513e5", - "GUID:ac3295688c7c22745a96e6ac34718181" + "GUID:ac3295688c7c22745a96e6ac34718181", + "GUID:75469ad4d38634e559750d17036d5f7c", + "GUID:f3634757d00dab2429c6c11e69404e97", + "GUID:91cf8206af184dac8e30eb46747e9939", + "GUID:0d87731a01a547bd97421cb01c596850", + "GUID:029c1c1b674aaae47a6841a0b89ad80e", + "GUID:f95d9c0cf55f3455cbbb2b2bfc56e55c", + "GUID:766b242fb43af451aaa331f39872177d", + "GUID:977f79b5e6de8468d8967dfff5ca8128", + "GUID:828002f0a2864c3cbbca4d1de01c7455", + "GUID:e9db755bba99425db4ca5d30e48b7429", + "GUID:e0eedfa2deb9406daf86fd8368728e39", + "GUID:26b8f796ecc6b4a2ab621c9a7e70fd11", + "GUID:e7751264a6735a942a64770d71eb49e0" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Explorer/Assets/DCL/Friends/DefaultFriendsEventBus.cs b/Explorer/Assets/DCL/Friends/DefaultFriendsEventBus.cs index da72abeb2e..9262767c60 100644 --- a/Explorer/Assets/DCL/Friends/DefaultFriendsEventBus.cs +++ b/Explorer/Assets/DCL/Friends/DefaultFriendsEventBus.cs @@ -5,36 +5,56 @@ namespace DCL.Friends public class DefaultFriendsEventBus : IFriendsEventBus { public event Action? OnFriendRequestReceived; - public event Action? OnFriendRequestSent; - public event Action? OnFriendRequestAccepted; - public event Action? OnFriendRequestRejected; - public event Action? OnFriendRequestCanceled; - public event Action? OnFriendRequestRemoved; - public event Action? OnFriendConnected; - public event Action? OnFriendDisconnected; + public event IFriendsEventBus.UserIdOperation? OnOtherUserRemovedTheFriendship; + public event IFriendsEventBus.UserIdOperation? OnOtherUserAcceptedYourRequest; + public event IFriendsEventBus.UserIdOperation? OnOtherUserRejectedYourRequest; + public event IFriendsEventBus.UserIdOperation? OnOtherUserCancelledTheRequest; + public event Action? OnYouSentFriendRequestToOtherUser; + public event IFriendsEventBus.UserIdOperation? OnYouRemovedFriend; + public event IFriendsEventBus.UserIdOperation? OnYouCancelledFriendRequestSentToOtherUser; + public event IFriendsEventBus.UserIdOperation? OnYouAcceptedFriendRequestReceivedFromOtherUser; + public event IFriendsEventBus.UserIdOperation? OnYouRejectedFriendRequestReceivedFromOtherUser; + public event Action? OnFriendConnected; + public event Action? OnFriendDisconnected; + public event Action? OnFriendAway; public void BroadcastFriendRequestReceived(FriendRequest request) => OnFriendRequestReceived?.Invoke(request); - public void BroadcastFriendRequestSent(FriendRequest request) => - OnFriendRequestSent?.Invoke(request); + public void BroadcastThatYouSentFriendRequestToOtherUser(FriendRequest request) => + OnYouSentFriendRequestToOtherUser?.Invoke(request); - public void BroadcastFriendRequestAccepted(string friendId) => - OnFriendRequestAccepted?.Invoke(friendId); + public void BroadcastThatYouRemovedFriend(string userId) => + OnYouRemovedFriend?.Invoke(userId); - public void BroadcastFriendRequestRejected(string friendId) => - OnFriendRequestRejected?.Invoke(friendId); + public void BroadcastThatYouAcceptedFriendRequestReceivedFromOtherUser(string userId) => + OnYouAcceptedFriendRequestReceivedFromOtherUser?.Invoke(userId); - public void BroadcastFriendRequestCanceled(string friendId) => - OnFriendRequestCanceled?.Invoke(friendId); + public void BroadcastThatYouCancelledFriendRequestSentToOtherUser(string userId) => + OnYouCancelledFriendRequestSentToOtherUser?.Invoke(userId); - public void BroadcastFriendRequestRemoved(string friendId) => - OnFriendRequestRemoved?.Invoke(friendId); + public void BroadcastThatYouRejectedFriendRequestReceivedFromOtherUser(string userId) => + OnYouRejectedFriendRequestReceivedFromOtherUser?.Invoke(userId); - public void BroadcastFriendConnected(string friendId) => - OnFriendConnected?.Invoke(friendId); + public void BroadcastThatOtherUserAcceptedYourRequest(string userId) => + OnOtherUserAcceptedYourRequest?.Invoke(userId); - public void BroadcastFriendDisconnected(string friendId) => - OnFriendDisconnected?.Invoke(friendId); + public void BroadcastThatOtherUserRejectedYourRequest(string userId) => + OnOtherUserRejectedYourRequest?.Invoke(userId); + + public void BroadcastThatOtherUserCancelledTheRequest(string userId) => + OnOtherUserCancelledTheRequest?.Invoke(userId); + + public void BroadcastThatOtherUserRemovedTheFriendship(string userId) => + OnOtherUserRemovedTheFriendship?.Invoke(userId); + + public void BroadcastFriendConnected(FriendProfile friend) => + OnFriendConnected?.Invoke(friend); + + public void BroadcastFriendDisconnected(FriendProfile friend) => + OnFriendDisconnected?.Invoke(friend); + + public void BroadcastFriendAsAway(FriendProfile friend) => + OnFriendAway?.Invoke(friend); } } diff --git a/Explorer/Assets/DCL/Friends/FriendProfile.cs b/Explorer/Assets/DCL/Friends/FriendProfile.cs new file mode 100644 index 0000000000..5785a614a0 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/FriendProfile.cs @@ -0,0 +1,35 @@ +using CommunicationData.URLHelpers; +using DCL.Web3; + +namespace DCL.Friends +{ + public class FriendProfile + { + public Web3Address Address { get; } + public string Name { get; } + public bool HasClaimedName { get; } + public URLAddress FacePictureUrl { get; } + + public FriendProfile(Web3Address address, string name, bool hasClaimedName, URLAddress facePictureUrl) + { + Address = address; + Name = name; + HasClaimedName = hasClaimedName; + FacePictureUrl = facePictureUrl; + } + + private bool Equals(FriendProfile other) => + Address.Equals(other.Address); + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((FriendProfile) obj); + } + + public override int GetHashCode() => + Address.GetHashCode(); + } +} diff --git a/Explorer/Assets/DCL/Friends/FriendProfile.cs.meta b/Explorer/Assets/DCL/Friends/FriendProfile.cs.meta new file mode 100644 index 0000000000..fd0f2ad5de --- /dev/null +++ b/Explorer/Assets/DCL/Friends/FriendProfile.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2e8bfa1c10bb4d259aab481830046a5d +timeCreated: 1737643827 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/FriendRequest.cs b/Explorer/Assets/DCL/Friends/FriendRequest.cs index 9b3f53fa5e..94f6027a07 100644 --- a/Explorer/Assets/DCL/Friends/FriendRequest.cs +++ b/Explorer/Assets/DCL/Friends/FriendRequest.cs @@ -20,12 +20,12 @@ public override int GetHashCode() => public string FriendRequestId { get; } public DateTime Timestamp { get; } - public string From { get; } - public string To { get; } - public string MessageBody { get; } + public FriendProfile From { get; } + public FriendProfile To { get; } + public string? MessageBody { get; } public bool HasBodyMessage => !string.IsNullOrEmpty(MessageBody); - public FriendRequest(string friendRequestId, DateTime timestamp, string from, string to, string messageBody) + public FriendRequest(string friendRequestId, DateTime timestamp, FriendProfile from, FriendProfile to, string? messageBody) { FriendRequestId = friendRequestId; Timestamp = timestamp; @@ -33,11 +33,5 @@ public FriendRequest(string friendRequestId, DateTime timestamp, string from, st To = to; MessageBody = messageBody; } - - public bool IsSentTo(string userId) => - To == userId; - - public bool IsReceivedFrom(string userId) => - From == userId; } } diff --git a/Explorer/Assets/DCL/Friends/FriendsCache.cs b/Explorer/Assets/DCL/Friends/FriendsCache.cs index 1a281f4e45..7234c62290 100644 --- a/Explorer/Assets/DCL/Friends/FriendsCache.cs +++ b/Explorer/Assets/DCL/Friends/FriendsCache.cs @@ -7,11 +7,9 @@ public class FriendsCache : ICollection, IReadOnlyCollection { private readonly HashSet friends = new (); - int ICollection.Count => friends.Count; - public bool IsReadOnly => false; - int IReadOnlyCollection.Count => friends.Count; + public int Count => friends.Count; public IEnumerator GetEnumerator() => friends.GetEnumerator(); diff --git a/Explorer/Assets/DCL/Friends/FriendsConnectivityStatusTracker.cs b/Explorer/Assets/DCL/Friends/FriendsConnectivityStatusTracker.cs new file mode 100644 index 0000000000..c4c8c70d87 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/FriendsConnectivityStatusTracker.cs @@ -0,0 +1,74 @@ +using DCL.Friends.UI.FriendPanel; +using System; +using System.Collections.Generic; + +namespace DCL.Friends +{ + public class FriendsConnectivityStatusTracker : IFriendsConnectivityStatusTracker, IDisposable + { + private readonly IFriendsEventBus friendEventBus; + private readonly Dictionary friendsOnlineStatus = new (); + + public event Action? OnFriendBecameOnline; + public event Action? OnFriendBecameAway; + public event Action? OnFriendBecameOffline; + + public FriendsConnectivityStatusTracker(IFriendsEventBus friendEventBus, + bool isConnectivityStatusEnabled) + { + this.friendEventBus = friendEventBus; + + if (!isConnectivityStatusEnabled) return; + + friendEventBus.OnFriendConnected += FriendBecameOnline; + friendEventBus.OnFriendAway += FriendBecameAway; + friendEventBus.OnFriendDisconnected += FriendBecameOffline; + + friendEventBus.OnYouRemovedFriend += FriendRemoved; + friendEventBus.OnOtherUserRemovedTheFriendship += FriendRemoved; + } + + public void Dispose() + { + friendEventBus.OnFriendConnected -= FriendBecameOnline; + friendEventBus.OnFriendAway -= FriendBecameAway; + friendEventBus.OnFriendDisconnected -= FriendBecameOffline; + + friendEventBus.OnYouRemovedFriend -= FriendRemoved; + friendEventBus.OnOtherUserRemovedTheFriendship -= FriendRemoved; + } + + private void FriendRemoved(string userid) => + friendsOnlineStatus.Remove(userid); + + public OnlineStatus GetFriendStatus(string friendAddress) => + friendsOnlineStatus.GetValueOrDefault(friendAddress, OnlineStatus.OFFLINE); + + private bool FriendOnlineStatusChanged(FriendProfile friendProfile, OnlineStatus onlineStatus) + { + if (friendsOnlineStatus.TryGetValue(friendProfile.Address, out OnlineStatus currentStatus) && currentStatus == onlineStatus) + return false; + + friendsOnlineStatus[friendProfile.Address] = onlineStatus; + return true; + } + + private void FriendBecameOnline(FriendProfile friendProfile) + { + if (FriendOnlineStatusChanged(friendProfile, OnlineStatus.ONLINE)) + OnFriendBecameOnline?.Invoke(friendProfile); + } + + private void FriendBecameAway(FriendProfile friendProfile) + { + if (FriendOnlineStatusChanged(friendProfile, OnlineStatus.AWAY)) + OnFriendBecameAway?.Invoke(friendProfile); + } + + private void FriendBecameOffline(FriendProfile friendProfile) + { + if (FriendOnlineStatusChanged(friendProfile, OnlineStatus.OFFLINE)) + OnFriendBecameOffline?.Invoke(friendProfile); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/FriendsConnectivityStatusTracker.cs.meta b/Explorer/Assets/DCL/Friends/FriendsConnectivityStatusTracker.cs.meta new file mode 100644 index 0000000000..289a3cbd06 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/FriendsConnectivityStatusTracker.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 127f165df89c4d9bb1591b1ee25c276d +timeCreated: 1739262373 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/IFriendsConnectivityStatusTracker.cs b/Explorer/Assets/DCL/Friends/IFriendsConnectivityStatusTracker.cs new file mode 100644 index 0000000000..fde2ed5e76 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/IFriendsConnectivityStatusTracker.cs @@ -0,0 +1,14 @@ +using DCL.Friends.UI.FriendPanel; +using System; + +namespace DCL.Friends +{ + public interface IFriendsConnectivityStatusTracker + { + event Action? OnFriendBecameOnline; + event Action? OnFriendBecameAway; + event Action? OnFriendBecameOffline; + + OnlineStatus GetFriendStatus(string friendAddress); + } +} diff --git a/Explorer/Assets/DCL/Friends/IFriendsConnectivityStatusTracker.cs.meta b/Explorer/Assets/DCL/Friends/IFriendsConnectivityStatusTracker.cs.meta new file mode 100644 index 0000000000..78c5f3d2ba --- /dev/null +++ b/Explorer/Assets/DCL/Friends/IFriendsConnectivityStatusTracker.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a1c8ff5efcd4e91b37a350ba4f5e1ac +timeCreated: 1739263054 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/IFriendsEventBus.cs b/Explorer/Assets/DCL/Friends/IFriendsEventBus.cs index 223f39bb1d..b866e07906 100644 --- a/Explorer/Assets/DCL/Friends/IFriendsEventBus.cs +++ b/Explorer/Assets/DCL/Friends/IFriendsEventBus.cs @@ -4,29 +4,51 @@ namespace DCL.Friends { public interface IFriendsEventBus { + public delegate void UserIdOperation(string userId); + + /// + /// Other user sent you a friend request + /// event Action OnFriendRequestReceived; - event Action OnFriendRequestSent; - event Action OnFriendRequestAccepted; - event Action OnFriendRequestRejected; - event Action OnFriendRequestCanceled; - event Action OnFriendRequestRemoved; - event Action OnFriendConnected; - event Action OnFriendDisconnected; + event UserIdOperation OnOtherUserAcceptedYourRequest; + event UserIdOperation OnOtherUserRejectedYourRequest; + event UserIdOperation OnOtherUserCancelledTheRequest; + event UserIdOperation OnOtherUserRemovedTheFriendship; + + event Action OnYouSentFriendRequestToOtherUser; + event UserIdOperation OnYouRemovedFriend; + event UserIdOperation OnYouCancelledFriendRequestSentToOtherUser; + event UserIdOperation OnYouAcceptedFriendRequestReceivedFromOtherUser; + event UserIdOperation OnYouRejectedFriendRequestReceivedFromOtherUser; + + event Action OnFriendConnected; + event Action OnFriendDisconnected; + event Action OnFriendAway; void BroadcastFriendRequestReceived(FriendRequest request); - void BroadcastFriendRequestSent(FriendRequest request); + void BroadcastThatOtherUserAcceptedYourRequest(string userId); + + void BroadcastThatOtherUserRejectedYourRequest(string userId); + + void BroadcastThatOtherUserCancelledTheRequest(string userId); + + void BroadcastThatOtherUserRemovedTheFriendship(string userId); + + void BroadcastFriendConnected(FriendProfile friend); + + void BroadcastFriendDisconnected(FriendProfile friend); - void BroadcastFriendRequestAccepted(string friendId); + void BroadcastFriendAsAway(FriendProfile friend); - void BroadcastFriendRequestRejected(string friendId); + void BroadcastThatYouSentFriendRequestToOtherUser(FriendRequest request); - void BroadcastFriendRequestCanceled(string friendId); + void BroadcastThatYouRemovedFriend(string userId); - void BroadcastFriendRequestRemoved(string friendId); + void BroadcastThatYouAcceptedFriendRequestReceivedFromOtherUser(string userId); - void BroadcastFriendConnected(string friendId); + void BroadcastThatYouCancelledFriendRequestSentToOtherUser(string userId); - void BroadcastFriendDisconnected(string friendId); + void BroadcastThatYouRejectedFriendRequestReceivedFromOtherUser(string userId); } } diff --git a/Explorer/Assets/DCL/Friends/IFriendsService.cs b/Explorer/Assets/DCL/Friends/IFriendsService.cs index 431e260d70..03fc57801c 100644 --- a/Explorer/Assets/DCL/Friends/IFriendsService.cs +++ b/Explorer/Assets/DCL/Friends/IFriendsService.cs @@ -1,5 +1,4 @@ using Cysharp.Threading.Tasks; -using DCL.Profiles; using System; using System.Collections.Generic; using System.Threading; @@ -9,10 +8,6 @@ namespace DCL.Friends { public interface IFriendsService : IDisposable { - UniTask GetOnlineFriendsAsync(int pageNum, int pageSize, CancellationToken ct); - - UniTask GetOfflineFriendsAsync(int pageNum, int pageSize, CancellationToken ct); - UniTask GetFriendsAsync(int pageNum, int pageSize, CancellationToken ct); UniTask GetMutualFriendsAsync(string userId, int pageNum, int pageSize, CancellationToken ct); @@ -36,21 +31,21 @@ public interface IFriendsService : IDisposable public readonly struct PaginatedFriendsResult : IDisposable { - private readonly List friends; + private readonly List friends; - public IReadOnlyList Friends => friends; + public IReadOnlyList Friends => friends; public int TotalAmount { get; } - public PaginatedFriendsResult(IEnumerable profiles, int totalAmount) + public PaginatedFriendsResult(IEnumerable profiles, int totalAmount) { - friends = ListPool.Get(); + friends = ListPool.Get(); friends.AddRange(profiles); TotalAmount = totalAmount; } public void Dispose() { - ListPool.Release(friends); + ListPool.Release(friends); } } diff --git a/Explorer/Assets/DCL/Friends/IPassportBridge.cs b/Explorer/Assets/DCL/Friends/IPassportBridge.cs new file mode 100644 index 0000000000..ce5960b353 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/IPassportBridge.cs @@ -0,0 +1,11 @@ +using Cysharp.Threading.Tasks; +using DCL.Web3; + +namespace DCL.Friends +{ + // We need the interface as the implementation must be in a different assembly, otherwise we get cyclic dependencies + public interface IPassportBridge + { + UniTask ShowAsync(Web3Address userId); + } +} diff --git a/Explorer/Assets/DCL/Friends/IPassportBridge.cs.meta b/Explorer/Assets/DCL/Friends/IPassportBridge.cs.meta new file mode 100644 index 0000000000..416ffa37aa --- /dev/null +++ b/Explorer/Assets/DCL/Friends/IPassportBridge.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ac25d141132e44ba8a1438879a3ae915 +timeCreated: 1738088300 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/IProfileThumbnailCache.cs b/Explorer/Assets/DCL/Friends/IProfileThumbnailCache.cs new file mode 100644 index 0000000000..87f9eaa78c --- /dev/null +++ b/Explorer/Assets/DCL/Friends/IProfileThumbnailCache.cs @@ -0,0 +1,15 @@ +using Cysharp.Threading.Tasks; +using DCL.Profiles; +using System.Threading; +using UnityEngine; + +namespace DCL.Friends +{ + public interface IProfileThumbnailCache + { + Sprite? GetThumbnail(string userId); + UniTask GetThumbnailAsync(Profile profile, CancellationToken ct = default); + UniTask GetThumbnailAsync(string userId, string thumbnailUrl, CancellationToken ct = default); + void SetThumbnail(string userId, Sprite sprite); + } +} diff --git a/Explorer/Assets/DCL/Friends/IProfileThumbnailCache.cs.meta b/Explorer/Assets/DCL/Friends/IProfileThumbnailCache.cs.meta new file mode 100644 index 0000000000..a0de4c622a --- /dev/null +++ b/Explorer/Assets/DCL/Friends/IProfileThumbnailCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 97bf94cc76b145a585a6eee26f556e74 +timeCreated: 1737718610 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/Passport.meta b/Explorer/Assets/DCL/Friends/Passport.meta new file mode 100644 index 0000000000..909669d687 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Passport.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 74c5056f49734c6da435d79f7d420080 +timeCreated: 1738087877 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/Passport/DCL.Friends.Passport.asmdef b/Explorer/Assets/DCL/Friends/Passport/DCL.Friends.Passport.asmdef new file mode 100644 index 0000000000..5d5ff54ea9 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Passport/DCL.Friends.Passport.asmdef @@ -0,0 +1,27 @@ +{ + "name": "DCL.Friends.Passport", + "rootNamespace": "", + "references": [ + "GUID:8322ea9340a544c59ddc56d4793eac74", + "GUID:3c7b57a14671040bd8c549056adc04f5", + "GUID:f51ebe6a0ceec4240a699833d6309b23", + "GUID:fa7b3fdbb04d67549916da7bd2af58ab", + "GUID:5ab29fa8ae5769b49ab29e390caca7a4", + "GUID:ca4e81cdd6a34d1aa54c32ad41fc5b3b", + "GUID:3640f3c0b42946b0b8794a1ed8e06ca5", + "GUID:4a12c0b1b77ec6b418a8d7bd5c925be3", + "GUID:166b65e6dfc848bb9fb075f53c293a38", + "GUID:ae249ee11a6e0ea4aa01cefc0246a151", + "GUID:ead265f036164eb7899edc341131f4d5", + "GUID:45f6fff651a0a514f8edfdaf9cce45af" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/Passport/DCL.Friends.Passport.asmdef.meta b/Explorer/Assets/DCL/Friends/Passport/DCL.Friends.Passport.asmdef.meta new file mode 100644 index 0000000000..78b2614bff --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Passport/DCL.Friends.Passport.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6495f30b61f24ad388388f1ab06a3b31 +timeCreated: 1738087896 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/Passport/MVCPassportBridge.cs b/Explorer/Assets/DCL/Friends/Passport/MVCPassportBridge.cs new file mode 100644 index 0000000000..456dbf69be --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Passport/MVCPassportBridge.cs @@ -0,0 +1,22 @@ +using Cysharp.Threading.Tasks; +using DCL.Passport; +using DCL.Web3; +using MVC; + +namespace DCL.Friends.Passport +{ + public class MVCPassportBridge : IPassportBridge + { + private readonly IMVCManager mvcManager; + + public MVCPassportBridge(IMVCManager mvcManager) + { + this.mvcManager = mvcManager; + } + + public async UniTask ShowAsync(Web3Address userId) + { + await mvcManager.ShowAsync(PassportController.IssueCommand(new PassportController.Params(userId.ToString()))); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/Passport/MVCPassportBridge.cs.meta b/Explorer/Assets/DCL/Friends/Passport/MVCPassportBridge.cs.meta new file mode 100644 index 0000000000..ac34155f6b --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Passport/MVCPassportBridge.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a76095719d704424bac34a8bdfaa43d8 +timeCreated: 1738088527 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/ProfileImageViewExtensions.cs b/Explorer/Assets/DCL/Friends/ProfileImageViewExtensions.cs new file mode 100644 index 0000000000..fd81c0051a --- /dev/null +++ b/Explorer/Assets/DCL/Friends/ProfileImageViewExtensions.cs @@ -0,0 +1,38 @@ +using CommunicationData.URLHelpers; +using Cysharp.Threading.Tasks; +using DCL.UI; +using DCL.Web3; +using System; +using System.Threading; +using UnityEngine; + +namespace DCL.Friends +{ + public static class ProfileImageViewExtensions + { + public static async UniTask LoadThumbnailSafeAsync(this ImageView imageView, IProfileThumbnailCache thumbnailCache, + Web3Address userId, URLAddress thumbnailUrl, CancellationToken ct) + { + try + { + imageView.IsLoading = true; + imageView.ImageEnabled = false; + + Sprite? sprite = await thumbnailCache.GetThumbnailAsync(userId, thumbnailUrl, ct); + + imageView.ImageEnabled = sprite != null; + + if (sprite != null) + imageView.SetImage(sprite); + } + catch (Exception) + { + imageView.ImageEnabled = false; + } + finally + { + imageView.IsLoading = false; + } + } + } +} diff --git a/Explorer/Assets/DCL/Friends/ProfileImageViewExtensions.cs.meta b/Explorer/Assets/DCL/Friends/ProfileImageViewExtensions.cs.meta new file mode 100644 index 0000000000..3155ce5cdf --- /dev/null +++ b/Explorer/Assets/DCL/Friends/ProfileImageViewExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 621736693a0f44f89274110f1453b7f5 +timeCreated: 1738950944 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/ProfileThumbnailCache.cs b/Explorer/Assets/DCL/Friends/ProfileThumbnailCache.cs new file mode 100644 index 0000000000..bd9f605197 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/ProfileThumbnailCache.cs @@ -0,0 +1,80 @@ +using CommunicationData.URLHelpers; +using Cysharp.Threading.Tasks; +using DCL.Diagnostics; +using DCL.Profiles; +using DCL.WebRequests; +using Plugins.TexturesFuse.TexturesServerWrap.Unzips; +using System; +using System.Collections.Generic; +using System.Threading; +using UnityEngine; +using Utility; + +namespace DCL.Friends +{ + public class ProfileThumbnailCache : IProfileThumbnailCache + { + private const int PIXELS_PER_UNIT = 50; + + private readonly IWebRequestController webRequestController; + private readonly Dictionary thumbnails = new (); + + public ProfileThumbnailCache(IWebRequestController webRequestController) + { + this.webRequestController = webRequestController; + } + + public Sprite? GetThumbnail(string userId) => + thumbnails.GetValueOrDefault(userId); + + public async UniTask GetThumbnailAsync(Profile profile, CancellationToken ct) + { + Sprite? sprite = GetThumbnail(profile.UserId); + if (sprite != null) + return sprite; + + return await DownloadThumbnailAsync(profile.UserId, profile.Avatar.FaceSnapshotUrl, ct); + } + + public async UniTask GetThumbnailAsync(string userId, string thumbnailUrl, CancellationToken ct) + { + Sprite? sprite = GetThumbnail(userId); + if (sprite != null) + return sprite; + + return await DownloadThumbnailAsync(userId, thumbnailUrl, ct); + } + + private async UniTask DownloadThumbnailAsync(string userId, string thumbnailUrl, CancellationToken ct) + { + if (URLAddress.EMPTY.Equals(thumbnailUrl)) return null; + + try + { + IOwnedTexture2D ownedTexture = await webRequestController.GetTextureAsync( + new CommonArguments(URLAddress.FromString(thumbnailUrl)), + new GetTextureArguments(TextureType.Albedo), + GetTextureWebRequest.CreateTexture(TextureWrapMode.Clamp), + ct, + ReportCategory.UI + ); + + var texture = ownedTexture.Texture; + texture.filterMode = FilterMode.Bilinear; + Sprite downloadedSprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), VectorUtilities.OneHalf, PIXELS_PER_UNIT, 0, SpriteMeshType.FullRect, Vector4.one, false); + SetThumbnail(userId, downloadedSprite); + + return downloadedSprite; + } + catch (Exception e) + { + ReportHub.LogException(e, new ReportData(ReportCategory.FRIENDS)); + return null; + } + } + + public void SetThumbnail(string userId, Sprite sprite) => + thumbnails[userId] = sprite; + + } +} diff --git a/Explorer/Assets/DCL/Friends/ProfileThumbnailCache.cs.meta b/Explorer/Assets/DCL/Friends/ProfileThumbnailCache.cs.meta new file mode 100644 index 0000000000..8638fbdc88 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/ProfileThumbnailCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3319c783d0634203ac0ab7e63dc2ca81 +timeCreated: 1737718675 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/RPCFriendsService.cs b/Explorer/Assets/DCL/Friends/RPCFriendsService.cs index 58e5b11476..580ce401a7 100644 --- a/Explorer/Assets/DCL/Friends/RPCFriendsService.cs +++ b/Explorer/Assets/DCL/Friends/RPCFriendsService.cs @@ -1,15 +1,18 @@ using CommunicationData.URLHelpers; using Cysharp.Threading.Tasks; +using DCL.Diagnostics; using DCL.Profiles; +using DCL.Profiles.Self; +using DCL.Web3; using DCL.Web3.Chains; using DCL.Web3.Identities; using Decentraland.SocialService.V2; +using Google.Protobuf.Collections; using Google.Protobuf.WellKnownTypes; using Newtonsoft.Json; using rpc_csharp; using System; using System.Collections.Generic; -using System.Linq; using System.Net.WebSockets; using System.Threading; @@ -26,96 +29,235 @@ public class RPCFriendsService : IFriendsService private const string UPDATE_FRIENDSHIP_PROCEDURE_NAME = "UpsertFriendship"; private const string SUBSCRIBE_FRIENDSHIP_UPDATES_PROCEDURE_NAME = "SubscribeToFriendshipUpdates"; private const string GET_MUTUAL_FRIENDS_PROCEDURE_NAME = "GetMutualFriends"; + private const string SUBSCRIBE_TO_CONNECTIVITY_UPDATES = "SubscribeToFriendConnectivityUpdates"; + private const int CONNECTION_TIMEOUT_SECS = 10; + private const int CONNECTION_RETRIES = 3; + private const int RETRY_STREAM_THROTTLE_MS = 30000; + private readonly URLAddress apiUrl; private readonly IFriendsEventBus eventBus; - private readonly IProfileRepository profileRepository; private readonly IWeb3IdentityCache identityCache; private readonly FriendsCache friendsCache; - private readonly WebSocketRpcTransport transport; - private readonly RpcClient client; - private readonly List> fetchProfileTasks = new (); - private readonly List friendRequestsBuffer = new (); - private readonly Dictionary authChainBuffer = new (); - private readonly HashSet allFriendsBuffer = new (); + private readonly ISelfProfile selfProfile; + private readonly List receivedFriendRequestsBuffer = new(); + private readonly List sentFriendRequestsBuffer = new(); + private readonly Dictionary authChainBuffer = new(); + private readonly List friendProfileBuffer = new(); + private readonly SemaphoreSlim handshakeMutex = new(1, 1); private RpcClientModule? module; + private RpcClientPort? port; + private WebSocketRpcTransport? transport; + private RpcClient? client; + + private bool isConnectionReady => transport?.State == WebSocketState.Open + && module != null + && client != null + && port != null; public RPCFriendsService(URLAddress apiUrl, IFriendsEventBus eventBus, - IProfileRepository profileRepository, IWeb3IdentityCache identityCache, - FriendsCache friendsCache) + FriendsCache friendsCache, + ISelfProfile selfProfile) { + this.apiUrl = apiUrl; this.eventBus = eventBus; - this.profileRepository = profileRepository; this.identityCache = identityCache; this.friendsCache = friendsCache; - transport = new WebSocketRpcTransport(new Uri(apiUrl)); - client = new RpcClient(transport); + this.selfProfile = selfProfile; } public void Dispose() { - transport.Dispose(); - client.Dispose(); + transport?.Dispose(); + client?.Dispose(); + } + + public async UniTask DisconnectAsync(CancellationToken ct) + { + try + { + await handshakeMutex.WaitAsync(ct); + + port?.Close(); + port = null; + module = null; + + if (transport != null) + { + await transport.CloseAsync(ct); + transport.Dispose(); + transport = null; + } + + client?.Dispose(); + client = null; + } + finally + { + handshakeMutex.Release(); + } } - // TODO: subscribe to friend's connectivity status of the rpc service whenever its available public async UniTask SubscribeToIncomingFriendshipEventsAsync(CancellationToken ct) { - await EnsureRpcConnectionAsync(ct); + // We try to keep the stream open until cancellation is requested + // If by any reason the rpc connection has a problem, we need to wait until it is restored, so we re-open the stream + while (!ct.IsCancellationRequested) + { + try + { + await EnsureRpcConnectionAsync(ct); + await OpenStreamAndProcessUpdatesAsync(); + } + catch (Exception e) when (e is not OperationCanceledException) + { + ReportHub.LogException(e, new ReportData(ReportCategory.FRIENDS)); + } + + await UniTask.Delay(RETRY_STREAM_THROTTLE_MS, cancellationToken: ct); + } + + return; + + async UniTask OpenStreamAndProcessUpdatesAsync() + { + IUniTaskAsyncEnumerable stream = + module!.CallServerStream(SUBSCRIBE_FRIENDSHIP_UPDATES_PROCEDURE_NAME, + new Empty()); + + await foreach (var response in stream.WithCancellation(ct)) + { + try + { + switch (response.UpdateCase) + { + case FriendshipUpdate.UpdateOneofCase.Accept: + friendsCache.Add(response.Accept.User.Address); + eventBus.BroadcastThatOtherUserAcceptedYourRequest(response.Accept.User.Address); + break; + + case FriendshipUpdate.UpdateOneofCase.Cancel: + eventBus.BroadcastThatOtherUserCancelledTheRequest(response.Cancel.User.Address); + break; + + case FriendshipUpdate.UpdateOneofCase.Delete: + friendsCache.Remove(response.Delete.User.Address); + eventBus.BroadcastThatOtherUserRemovedTheFriendship(response.Delete.User.Address); + break; + + case FriendshipUpdate.UpdateOneofCase.Reject: + eventBus.BroadcastThatOtherUserRejectedYourRequest(response.Reject.User.Address); + break; + + case FriendshipUpdate.UpdateOneofCase.Request: + var request = response.Request; + + Profile? myProfile = await selfProfile.ProfileAsync(ct); + + var fr = new FriendRequest( + request.Id, + DateTimeOffset.FromUnixTimeMilliseconds(request.CreatedAt).DateTime, + ToClientFriendProfile(request.Friend), + ToClientFriendProfile(myProfile!), + request.HasMessage ? request.Message : string.Empty); + + eventBus.BroadcastFriendRequestReceived(fr); + break; + } + } - IUniTaskAsyncEnumerable stream = - module!.CallServerStream(SUBSCRIBE_FRIENDSHIP_UPDATES_PROCEDURE_NAME, new Empty()); + // Do exception handling as we need to keep the stream open in case we have an internal error in the processing of the data + catch (Exception e) when (e is not OperationCanceledException) + { + ReportHub.LogException(e, new ReportData(ReportCategory.FRIENDS)); + } + } + } + } - await foreach (var response in stream.WithCancellation(ct)) + public async UniTask SubscribeToConnectivityStatusAsync(CancellationToken ct) + { + // We try to keep the stream open until cancellation is requested + // If by any reason the rpc connection has a problem, we need to wait until it is restored, so we re-open the stream + while (!ct.IsCancellationRequested) { - switch (response.UpdateCase) + try + { + await EnsureRpcConnectionAsync(ct); + await OpenStreamAndProcessUpdatesAsync(); + } + catch (Exception e) when (e is not OperationCanceledException) { - case FriendshipUpdate.UpdateOneofCase.Accept: - friendsCache.Add(response.Accept.User.Address); - eventBus.BroadcastFriendRequestAccepted(response.Accept.User.Address); - break; + ReportHub.LogException(e, new ReportData(ReportCategory.FRIENDS)); + } - case FriendshipUpdate.UpdateOneofCase.Cancel: - eventBus.BroadcastFriendRequestCanceled(response.Cancel.User.Address); - break; + await UniTask.Delay(RETRY_STREAM_THROTTLE_MS, cancellationToken: ct); + } - case FriendshipUpdate.UpdateOneofCase.Delete: - friendsCache.Remove(response.Delete.User.Address); - eventBus.BroadcastFriendRequestRemoved(response.Delete.User.Address); - break; + return; - case FriendshipUpdate.UpdateOneofCase.Reject: - eventBus.BroadcastFriendRequestRejected(response.Reject.User.Address); - break; + async UniTask OpenStreamAndProcessUpdatesAsync() + { + IUniTaskAsyncEnumerable stream = + module!.CallServerStream(SUBSCRIBE_TO_CONNECTIVITY_UPDATES, new Empty()); - case FriendshipUpdate.UpdateOneofCase.Request: - var request = response.Request; + await foreach (var response in stream.WithCancellation(ct)) + { + try + { + switch (response.Status) + { + case ConnectivityStatus.Away: + eventBus.BroadcastFriendAsAway(ToClientFriendProfile(response.Friend)); + break; + case ConnectivityStatus.Offline: + eventBus.BroadcastFriendDisconnected(ToClientFriendProfile(response.Friend)); + break; + case ConnectivityStatus.Online: + eventBus.BroadcastFriendConnected(ToClientFriendProfile(response.Friend)); + break; + } + } - var fr = new FriendRequest( - GetFriendRequestId(request.User.Address, request.CreatedAt), - DateTimeOffset.FromUnixTimeSeconds(request.CreatedAt).DateTime, - request.User.Address, - identityCache.EnsuredIdentity().Address, - request.HasMessage ? request.Message : string.Empty); - - eventBus.BroadcastFriendRequestReceived(fr); - break; + // Do exception handling as we need to keep the stream open in case we have an internal error in the processing of the data + catch (Exception e) when (e is not OperationCanceledException) + { + ReportHub.LogException(e, new ReportData(ReportCategory.FRIENDS)); + } } } } - public async UniTask GetOnlineFriendsAsync(int pageNum, int pageSize, CancellationToken ct) => - await GetFriendsAsync(pageNum, pageSize, ConnectivityStatus.Online, ct); + public async UniTask GetFriendsAsync(int pageNum, int pageSize, CancellationToken ct) + { + await EnsureRpcConnectionAsync(ct); - public async UniTask GetOfflineFriendsAsync(int pageNum, int pageSize, CancellationToken ct) => - await GetFriendsAsync(pageNum, pageSize, ConnectivityStatus.Offline, ct); + var payload = new GetFriendsPayload + { + Pagination = new Pagination + { + Offset = pageNum * pageSize, + Limit = pageSize, + }, + }; + + var response = await module! + .CallUnaryProcedure(GET_FRIENDS_PROCEDURE_NAME, payload) + .AttachExternalCancellation(ct) + .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); + + foreach (var profile in response.Friends) + friendsCache.Add(profile.Address); - public async UniTask GetFriendsAsync(int pageNum, int pageSize, CancellationToken ct) => - await GetFriendsAsync(pageNum, pageSize, null, ct); + IEnumerable profiles = ToClientFriendProfiles(response.Friends); + + return new PaginatedFriendsResult(profiles, response.PaginationData.Total); + } - public async UniTask GetMutualFriendsAsync(string userId, int pageNum, int pageSize, CancellationToken ct) + public async UniTask GetMutualFriendsAsync(string userId, int pageNum, int pageSize, + CancellationToken ct) { await EnsureRpcConnectionAsync(ct); @@ -123,7 +265,7 @@ public async UniTask GetMutualFriendsAsync(string userId { Pagination = new Pagination { - Offset = (pageNum - 1) * pageSize, + Offset = pageNum * pageSize, Limit = pageSize, }, User = new User @@ -132,16 +274,12 @@ public async UniTask GetMutualFriendsAsync(string userId }, }; - PaginatedUsersResponse response = await module!.CallUnaryProcedure(GET_MUTUAL_FRIENDS_PROCEDURE_NAME, payload) - .AttachExternalCancellation(ct) - .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); - - allFriendsBuffer.Clear(); + var response = await module! + .CallUnaryProcedure(GET_MUTUAL_FRIENDS_PROCEDURE_NAME, payload) + .AttachExternalCancellation(ct) + .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); - foreach (var user in response.Users) - allFriendsBuffer.Add(user.Address); - - IEnumerable profiles = await FetchProfilesAsync(allFriendsBuffer, ct); + var profiles = ToClientFriendProfiles(response.Friends); return new PaginatedFriendsResult(profiles, response.PaginationData.Total); } @@ -158,14 +296,16 @@ public async UniTask GetFriendshipStatusAsync(string userId, C }, }; - GetFriendshipStatusResponse response = await module!.CallUnaryProcedure(GET_FRIENDSHIP_STATUS_PROCEDURE_NAME, payload) - .AttachExternalCancellation(ct) - .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); + GetFriendshipStatusResponse response = await module! + .CallUnaryProcedure(GET_FRIENDSHIP_STATUS_PROCEDURE_NAME, payload) + .AttachExternalCancellation(ct) + .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); switch (response.ResponseCase) { case GetFriendshipStatusResponse.ResponseOneofCase.InternalServerError: - throw new Exception($"Cannot fetch friendship status {response.ResponseCase}"); + throw new Exception( + $"Cannot fetch friendship status {response.ResponseCase}: {response.InternalServerError}"); case GetFriendshipStatusResponse.ResponseOneofCase.Accepted: switch (response.Accepted.Status) { @@ -185,19 +325,109 @@ public async UniTask GetFriendshipStatusAsync(string userId, C return FriendshipStatus.NONE; } - public async UniTask GetReceivedFriendRequestsAsync(int pageNum, int pageSize, CancellationToken ct) => - await GetFriendRequestsAsync(pageNum, pageSize, GET_RECEIVED_FRIEND_REQUESTS_PROCEDURE_NAME, ct); + public async UniTask GetReceivedFriendRequestsAsync(int pageNum, int pageSize, + CancellationToken ct) + { + await EnsureRpcConnectionAsync(ct); + + receivedFriendRequestsBuffer.Clear(); - public async UniTask GetSentFriendRequestsAsync(int pageNum, int pageSize, CancellationToken ct) => - await GetFriendRequestsAsync(pageNum, pageSize, GET_SENT_FRIEND_REQUESTS_PROCEDURE_NAME, ct); + var payload = new GetFriendshipRequestsPayload + { + Pagination = new Pagination + { + Offset = pageNum * pageSize, + Limit = pageSize, + }, + }; + + PaginatedFriendshipRequestsResponse response = await module! + .CallUnaryProcedure(GET_RECEIVED_FRIEND_REQUESTS_PROCEDURE_NAME, + payload) + .AttachExternalCancellation(ct) + .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); + + Profile? myProfile = await selfProfile.ProfileAsync(ct); + + switch (response.ResponseCase) + { + case PaginatedFriendshipRequestsResponse.ResponseOneofCase.Requests: + foreach (var rr in response.Requests.Requests) + { + var fr = new FriendRequest( + rr.Id, + DateTimeOffset.FromUnixTimeMilliseconds(rr.CreatedAt).DateTime, + ToClientFriendProfile(rr.Friend), + ToClientFriendProfile(myProfile!), + rr.Message); + + receivedFriendRequestsBuffer.Add(fr); + } + + break; + case PaginatedFriendshipRequestsResponse.ResponseOneofCase.InternalServerError: + default: + throw new Exception($"Cannot fetch received friend requests {response.ResponseCase}"); + } + + return new PaginatedFriendRequestsResult(receivedFriendRequestsBuffer, response.PaginationData?.Total ?? 0); + } + + public async UniTask GetSentFriendRequestsAsync(int pageNum, int pageSize, + CancellationToken ct) + { + await EnsureRpcConnectionAsync(ct); + + sentFriendRequestsBuffer.Clear(); + + var payload = new GetFriendshipRequestsPayload + { + Pagination = new Pagination + { + Offset = pageNum * pageSize, + Limit = pageSize, + }, + }; + + PaginatedFriendshipRequestsResponse response = await module! + .CallUnaryProcedure(GET_SENT_FRIEND_REQUESTS_PROCEDURE_NAME, + payload) + .AttachExternalCancellation(ct) + .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); + + Profile? myProfile = await selfProfile.ProfileAsync(ct); + + switch (response.ResponseCase) + { + case PaginatedFriendshipRequestsResponse.ResponseOneofCase.Requests: + foreach (var rr in response.Requests.Requests) + { + var fr = new FriendRequest( + rr.Id, + DateTimeOffset.FromUnixTimeMilliseconds(rr.CreatedAt).DateTime, + ToClientFriendProfile(myProfile!), + ToClientFriendProfile(rr.Friend), + rr.Message); + + sentFriendRequestsBuffer.Add(fr); + } + + break; + case PaginatedFriendshipRequestsResponse.ResponseOneofCase.InternalServerError: + default: + throw new Exception($"Cannot fetch received friend requests {response.ResponseCase}"); + } + + return new PaginatedFriendRequestsResult(sentFriendRequestsBuffer, response.PaginationData?.Total ?? 0); + } public async UniTask RejectFriendshipAsync(string friendId, CancellationToken ct) { await EnsureRpcConnectionAsync(ct); - await this.UpdateFriendshipAsync(new UpsertFriendshipPayload + await UpdateFriendshipAsync(new UpsertFriendshipPayload { - Request = new UpsertFriendshipPayload.Types.RequestPayload + Reject = new UpsertFriendshipPayload.Types.RejectPayload { User = new User { @@ -205,13 +435,15 @@ await this.UpdateFriendshipAsync(new UpsertFriendshipPayload }, }, }, ct); + + eventBus.BroadcastThatYouRejectedFriendRequestReceivedFromOtherUser(friendId); } public async UniTask CancelFriendshipAsync(string friendId, CancellationToken ct) { await EnsureRpcConnectionAsync(ct); - await this.UpdateFriendshipAsync(new UpsertFriendshipPayload + await UpdateFriendshipAsync(new UpsertFriendshipPayload { Cancel = new UpsertFriendshipPayload.Types.CancelPayload { @@ -221,13 +453,15 @@ await this.UpdateFriendshipAsync(new UpsertFriendshipPayload }, }, }, ct); + + eventBus.BroadcastThatYouCancelledFriendRequestSentToOtherUser(friendId); } public async UniTask AcceptFriendshipAsync(string friendId, CancellationToken ct) { await EnsureRpcConnectionAsync(ct); - await this.UpdateFriendshipAsync(new UpsertFriendshipPayload + await UpdateFriendshipAsync(new UpsertFriendshipPayload { Accept = new UpsertFriendshipPayload.Types.AcceptPayload { @@ -239,13 +473,15 @@ await this.UpdateFriendshipAsync(new UpsertFriendshipPayload }, ct); friendsCache.Add(friendId); + + eventBus.BroadcastThatYouAcceptedFriendRequestReceivedFromOtherUser(friendId); } public async UniTask DeleteFriendshipAsync(string friendId, CancellationToken ct) { await EnsureRpcConnectionAsync(ct); - await this.UpdateFriendshipAsync(new UpsertFriendshipPayload + await UpdateFriendshipAsync(new UpsertFriendshipPayload { Delete = new UpsertFriendshipPayload.Types.DeletePayload { @@ -257,9 +493,12 @@ await this.UpdateFriendshipAsync(new UpsertFriendshipPayload }, ct); friendsCache.Remove(friendId); + + eventBus.BroadcastThatYouRemovedFriend(friendId); } - public async UniTask RequestFriendshipAsync(string friendId, string messageBody, CancellationToken ct) + public async UniTask RequestFriendshipAsync(string friendId, string messageBody, + CancellationToken ct) { await EnsureRpcConnectionAsync(ct); @@ -275,39 +514,77 @@ public async UniTask RequestFriendshipAsync(string friendId, stri }, }, ct); - return new FriendRequest(GetFriendRequestId(friendId, response.CreatedAt), - DateTimeOffset.FromUnixTimeSeconds(response.CreatedAt).DateTime, - identityCache.EnsuredIdentity().Address, - friendId, + Profile? myProfile = await selfProfile.ProfileAsync(ct); + + var fr = new FriendRequest(response.Id, + DateTimeOffset.FromUnixTimeMilliseconds(response.CreatedAt).DateTime, + ToClientFriendProfile(myProfile!), + ToClientFriendProfile(response.Friend), messageBody); + + eventBus.BroadcastThatYouSentFriendRequestToOtherUser(fr); + + return fr; } private async UniTask EnsureRpcConnectionAsync(CancellationToken ct) { - switch (transport.State) - { - case WebSocketState.Open: - break; - case WebSocketState.Connecting: - await UniTask.WaitWhile(() => transport.State == WebSocketState.Connecting, cancellationToken: ct); - break; - default: - await transport.ConnectAsync(ct); - transport.ListenForIncomingData(); + var handshakeFinished = false; + int retries = CONNECTION_RETRIES; - // The service expects the auth-chain in json format within a 30 seconds threshold after connection - await transport.SendMessageAsync(BuildAuthChain(), ct); - break; - } - - if (module == null) + while (!handshakeFinished && retries > 0) { - RpcClientPort port = await client.CreatePort("friends"); - module = await port.LoadModule(RPC_SERVICE_NAME); + try + { + retries--; + await StartHandshakeAsync(); + handshakeFinished = true; + } + catch (WebSocketException) + { + if (retries == 0) + throw; + } + catch (TimeoutException) + { + if (retries == 0) + throw; + } } return; + async UniTask StartHandshakeAsync() + { + try + { + await handshakeMutex.WaitAsync(ct); + + if (!isConnectionReady) + { + client?.Dispose(); + transport?.Dispose(); + transport = new WebSocketRpcTransport(new Uri(apiUrl)); + client = new RpcClient(transport); + + await transport.ConnectAsync(ct).Timeout(TimeSpan.FromSeconds(CONNECTION_TIMEOUT_SECS)); + + string authChain = BuildAuthChain(); + // The service expects the auth-chain in json format within a 30 seconds threshold after connection + await transport.SendMessageAsync(authChain, ct); + + transport.ListenForIncomingData(); + + port = await client.CreatePort("friends"); + module = await port.LoadModule(RPC_SERVICE_NAME); + } + } + finally + { + handshakeMutex.Release(); + } + } + string BuildAuthChain() { authChainBuffer.Clear(); @@ -329,111 +606,51 @@ string BuildAuthChain() } } - private async UniTask GetFriendsAsync(int pageNum, int pageSize, ConnectivityStatus? connectivityStatus, CancellationToken ct) + private async UniTask UpdateFriendshipAsync( + UpsertFriendshipPayload payload, + CancellationToken ct) { - await EnsureRpcConnectionAsync(ct); + UpsertFriendshipResponse response = await module! + .CallUnaryProcedure(UPDATE_FRIENDSHIP_PROCEDURE_NAME, payload) + .AttachExternalCancellation(ct) + .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); - var payload = new GetFriendsPayload + return response.ResponseCase switch { - Pagination = new Pagination - { - Offset = (pageNum - 1) * pageSize, - Limit = pageSize, - }, + UpsertFriendshipResponse.ResponseOneofCase.Accepted => response.Accepted, + _ => throw new Exception($"Cannot update friendship {response.ResponseCase}") }; - - if (connectivityStatus != null) - payload.Status = connectivityStatus.Value; - - PaginatedUsersResponse response = await module!.CallUnaryProcedure(GET_FRIENDS_PROCEDURE_NAME, payload) - .AttachExternalCancellation(ct) - .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); - - allFriendsBuffer.Clear(); - - foreach (var user in response.Users) - { - allFriendsBuffer.Add(user.Address); - friendsCache.Add(user.Address); - } - - IEnumerable profiles = await FetchProfilesAsync(allFriendsBuffer, ct); - - return new PaginatedFriendsResult(profiles, response.PaginationData.Total); } - private async UniTask> FetchProfilesAsync(IEnumerable ids, CancellationToken ct) + private IEnumerable ToClientFriendProfiles( + RepeatedField friends) { - fetchProfileTasks.Clear(); + friendProfileBuffer.Clear(); - foreach (string userId in ids) - fetchProfileTasks.Add(profileRepository.GetAsync(userId, ct)); + foreach (Decentraland.SocialService.V2.FriendProfile profile in friends) + friendProfileBuffer.Add(ToClientFriendProfile(profile)); - return (await UniTask.WhenAll(fetchProfileTasks)) - .Where(profile => profile != null)!; + return friendProfileBuffer; } - private async UniTask GetFriendRequestsAsync(int pageNum, int pageSize, string procedureName, - CancellationToken ct) + private FriendProfile ToClientFriendProfile(Decentraland.SocialService.V2.FriendProfile profile) { - await EnsureRpcConnectionAsync(ct); - - friendRequestsBuffer.Clear(); + var fp = new FriendProfile(new Web3Address(profile.Address), + profile.Name, + profile.HasClaimedName, + URLAddress.FromString(profile.ProfilePictureUrl)); - var payload = new GetFriendshipRequestsPayload - { - Pagination = new Pagination - { - Offset = (pageNum - 1) * pageSize, - Limit = pageSize, - }, - }; - - PaginatedFriendshipRequestsResponse response = await module!.CallUnaryProcedure(procedureName, payload) - .AttachExternalCancellation(ct) - .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); - - switch (response.ResponseCase) - { - case PaginatedFriendshipRequestsResponse.ResponseOneofCase.Requests: - foreach (var rr in response.Requests.Requests) - { - var fr = new FriendRequest( - GetFriendRequestId(rr.User.Address, rr.CreatedAt), - DateTimeOffset.FromUnixTimeSeconds(rr.CreatedAt).DateTime, - rr.User.Address, - identityCache.EnsuredIdentity().Address, - rr.Message); - - friendRequestsBuffer.Add(fr); - } - - break; - case PaginatedFriendshipRequestsResponse.ResponseOneofCase.InternalServerError: - default: - throw new Exception($"Cannot fetch friend requests {procedureName} {response.ResponseCase}"); - } - - return new PaginatedFriendRequestsResult(friendRequestsBuffer, response.PaginationData.Total); + return fp; } - private async UniTask UpdateFriendshipAsync( - UpsertFriendshipPayload payload, - CancellationToken ct) + private FriendProfile ToClientFriendProfile(Profile profile) { - UpsertFriendshipResponse response = await module!.CallUnaryProcedure(UPDATE_FRIENDSHIP_PROCEDURE_NAME, payload) - .AttachExternalCancellation(ct) - .Timeout(TimeSpan.FromSeconds(TIMEOUT_SECONDS)); + var fp = new FriendProfile(new Web3Address(profile.UserId), + profile.Name, + profile.HasClaimedName, + profile.Avatar.FaceSnapshotUrl); - return response.ResponseCase switch - { - UpsertFriendshipResponse.ResponseOneofCase.Accepted => response.Accepted, - _ => throw new Exception($"Cannot update friendship {response.ResponseCase}") - }; + return fp; } - - // TODO: this method should be removed as the service has to return the request id - private static string GetFriendRequestId(string userId, long createdAt) => - $"{userId}-{createdAt}"; } } diff --git a/Explorer/Assets/DCL/Friends/Textures.meta b/Explorer/Assets/DCL/Friends/Textures.meta new file mode 100644 index 0000000000..355d2bbcfd --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 932ef5c0b7e1e4f75b5416b8bfeec1ea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/Textures/EmptyBlockedUsersIcon.png b/Explorer/Assets/DCL/Friends/Textures/EmptyBlockedUsersIcon.png new file mode 100644 index 0000000000..a54b34cfd4 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/EmptyBlockedUsersIcon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aed8ca10f687be35ef5af8d08a49e8ca42c64cb857bc4ca7e765e093edd3e923 +size 2008 diff --git a/Explorer/Assets/DCL/Friends/Textures/EmptyBlockedUsersIcon.png.meta b/Explorer/Assets/DCL/Friends/Textures/EmptyBlockedUsersIcon.png.meta new file mode 100644 index 0000000000..e37b502318 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/EmptyBlockedUsersIcon.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 77a17b25d704b4d57b6c9e8ca21d8262 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/Textures/Friends off.png b/Explorer/Assets/DCL/Friends/Textures/Friends off.png new file mode 100644 index 0000000000..f6746daa58 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/Friends off.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:831bec702de9773d45582e2e7627038d762381a289b22dc5d1942cb018a9d0ee +size 525 diff --git a/Explorer/Assets/DCL/Friends/Textures/Friends off.png.meta b/Explorer/Assets/DCL/Friends/Textures/Friends off.png.meta new file mode 100644 index 0000000000..6cf6ff7a68 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/Friends off.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 664d8ada18433407bbc74c6bee347d43 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/Textures/Friends on border.png b/Explorer/Assets/DCL/Friends/Textures/Friends on border.png new file mode 100644 index 0000000000..b93c605aac --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/Friends on border.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dca4ec55a218875df8846446bee81f3f2372c86c1634c2d5444dd2d9129241a9 +size 1974 diff --git a/Explorer/Assets/DCL/Friends/Textures/Friends on border.png.meta b/Explorer/Assets/DCL/Friends/Textures/Friends on border.png.meta new file mode 100644 index 0000000000..9c139227cb --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/Friends on border.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: ea976743b789b4758a0e63d518fb2d9c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/Textures/Friends on.png b/Explorer/Assets/DCL/Friends/Textures/Friends on.png new file mode 100644 index 0000000000..4c75c89171 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/Friends on.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e51d6e4059fe56437c1e00e324af5c4fe1dbcca3367d4a81b077ed6f8ffdd919 +size 625 diff --git a/Explorer/Assets/DCL/Friends/Textures/Friends on.png.meta b/Explorer/Assets/DCL/Friends/Textures/Friends on.png.meta new file mode 100644 index 0000000000..df144da433 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/Textures/Friends on.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 86dbb279b12d344ddb61ec75d52123b4 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio.meta b/Explorer/Assets/DCL/Friends/UI/Audio.meta new file mode 100644 index 0000000000..70defa093a --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ceb72d346a08045ba84776ac7a094d6c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/ContextMenu - Open.asset b/Explorer/Assets/DCL/Friends/UI/Audio/ContextMenu - Open.asset new file mode 100644 index 0000000000..9f499de206 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/ContextMenu - Open.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: ContextMenu - Open + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: 7f428ad41d0e2a94cb3f9761fc21f9b1, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0.05 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/ContextMenu - Open.asset.meta b/Explorer/Assets/DCL/Friends/UI/Audio/ContextMenu - Open.asset.meta new file mode 100644 index 0000000000..ca0ebd678a --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/ContextMenu - Open.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2c2582a131074b93b5be249c1e77b96 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Accepted.asset b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Accepted.asset new file mode 100644 index 0000000000..d2f54e1b30 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Accepted.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: FriendNotification - Accepted + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: 6aabe45353eca774e825c2f529aa7ec7, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Accepted.asset.meta b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Accepted.asset.meta new file mode 100644 index 0000000000..73c4647e6f --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Accepted.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b8c021e5ddb204814adf04f0793f36d3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Request.asset b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Request.asset new file mode 100644 index 0000000000..a8934eab31 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Request.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: FriendNotification - Request + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: abcf714583b0c734eab3343b58e27b02, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Request.asset.meta b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Request.asset.meta new file mode 100644 index 0000000000..d614998af0 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendNotification - Request.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b6493777141cb4794b18ce0545cc47d3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendOnline - PushNotification - Show.asset b/Explorer/Assets/DCL/Friends/UI/Audio/FriendOnline - PushNotification - Show.asset new file mode 100644 index 0000000000..3943dfb7c6 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendOnline - PushNotification - Show.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: FriendOnline - PushNotification - Show + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: 14732b805327118469e670c8a76f5163, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0.05 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendOnline - PushNotification - Show.asset.meta b/Explorer/Assets/DCL/Friends/UI/Audio/FriendOnline - PushNotification - Show.asset.meta new file mode 100644 index 0000000000..88b90bcd34 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendOnline - PushNotification - Show.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 33affe5a01ca04bcdaf10484c87793e2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Close.asset b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Close.asset new file mode 100644 index 0000000000..4f3c24b8c8 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Close.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: FriendPanel - Close + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: 0888e90d422192b4583bb9e189f4d249, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Close.asset.meta b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Close.asset.meta new file mode 100644 index 0000000000..dd856ec029 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Close.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9d16bdd7993a54999ac1e119599ef3c6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Open.asset b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Open.asset new file mode 100644 index 0000000000..32bf76496b --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Open.asset @@ -0,0 +1,20 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ea799a483c3f4db8afbf38b0e6c08665, type: 3} + m_Name: FriendPanel - Open + m_EditorClassIdentifier: + audioClips: + - {fileID: 8300000, guid: 590bf91ade06408408a1d23e10480116, type: 3} + relativeVolume: 1 + audioCategory: 0 + pitchVariation: 0 + clipSelectionMode: 0 diff --git a/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Open.asset.meta b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Open.asset.meta new file mode 100644 index 0000000000..1eb7885607 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/Audio/FriendPanel - Open.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 41bf79b9388dc4b009650f4dbe9b3155 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel.meta new file mode 100644 index 0000000000..d3c1848484 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc0f1c71a323e40e6b54af6da7ff8e3d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendPanelOnlineStatusSettings.asset b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendPanelOnlineStatusSettings.asset new file mode 100644 index 0000000000..e8b9779473 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendPanelOnlineStatusSettings.asset @@ -0,0 +1,27 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6b3be250445c46feb68af2caf5e5ec25, type: 3} + m_Name: FriendPanelOnlineStatusSettings + m_EditorClassIdentifier: + onlineStatusConfigurations: + - Status: 0 + Configuration: + StatusColor: {r: 0.34117648, g: 0.8745098, b: 0.25490198, a: 1} + StatusText: Online + - Status: 1 + Configuration: + StatusColor: {r: 1, g: 0.63529414, b: 0.3529412, a: 1} + StatusText: Away + - Status: 2 + Configuration: + StatusColor: {r: 0.627451, g: 0.60784316, b: 0.65882355, a: 1} + StatusText: Offline diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendPanelOnlineStatusSettings.asset.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendPanelOnlineStatusSettings.asset.meta new file mode 100644 index 0000000000..eee0e38bcd --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendPanelOnlineStatusSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bbca3b81bc8c54e1fb00e0cc1fa3f195 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelController.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelController.cs new file mode 100644 index 0000000000..4609d930aa --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelController.cs @@ -0,0 +1,242 @@ +using Cysharp.Threading.Tasks; +using DCL.Clipboard; +using DCL.Friends.Chat; +using DCL.Friends.UI.FriendPanel.Sections.Blocked; +using DCL.Friends.UI.FriendPanel.Sections.Friends; +using DCL.Friends.UI.FriendPanel.Sections.Requests; +using DCL.Multiplayer.Connectivity; +using DCL.Profiles; +using DCL.UI.Sidebar.SidebarActionsBus; +using DCL.Web3.Identities; +using DCL.WebRequests; +using ECS.SceneLifeCycle.Realm; +using MVC; +using System.Threading; +using UnityEngine.InputSystem; +using UnityEngine.Pool; +using Utility; + +namespace DCL.Friends.UI.FriendPanel +{ + public class FriendsPanelController : ControllerBase + { + public enum FriendsPanelTab + { + FRIENDS, + REQUESTS, + BLOCKED + } + + private const int FRIENDS_PAGE_SIZE = 50; + private const int FRIENDS_REQUEST_PAGE_SIZE = 100; + private const int FRIENDS_FETCH_ELEMENTS_THRESHOLD = 5; + + private readonly IChatLifecycleBusController chatLifecycleBusController; + private readonly NotificationIndicatorView sidebarRequestNotificationIndicator; + private readonly BlockedSectionController blockedSectionController; + private readonly FriendSectionController? friendSectionController; + private readonly FriendsSectionDoubleCollectionController? friendSectionControllerConnectivity; + private readonly RequestsSectionController requestsSectionController; + private readonly DCLInput dclInput; + private readonly ISidebarActionsBus sidebarActionsBus; + private readonly bool includeUserBlocking; + + private CancellationTokenSource friendsPanelCts = new (); + private UniTaskCompletionSource closeTaskCompletionSource = new (); + + public override CanvasOrdering.SortingLayer Layer => CanvasOrdering.SortingLayer.Persistent; + + public FriendsPanelController(ViewFactoryMethod viewFactory, + FriendsPanelView instantiatedView, + IChatLifecycleBusController chatLifecycleBusController, + NotificationIndicatorView sidebarRequestNotificationIndicator, + IFriendsService friendsService, + IFriendsEventBus friendEventBus, + IMVCManager mvcManager, + IWeb3IdentityCache web3IdentityCache, + IProfileRepository profileRepository, + ISystemClipboard systemClipboard, + IWebRequestController webRequestController, + IProfileThumbnailCache profileThumbnailCache, + DCLInput dclInput, + IPassportBridge passportBridge, + IOnlineUsersProvider onlineUsersProvider, + IRealmNavigator realmNavigator, + IFriendsConnectivityStatusTracker friendsConnectivityStatusTracker, + ISidebarActionsBus sidebarActionsBus, + bool includeUserBlocking, + bool isConnectivityStatusEnabled) : base(viewFactory) + { + this.chatLifecycleBusController = chatLifecycleBusController; + this.sidebarRequestNotificationIndicator = sidebarRequestNotificationIndicator; + this.dclInput = dclInput; + this.sidebarActionsBus = sidebarActionsBus; + this.includeUserBlocking = includeUserBlocking; + + if (isConnectivityStatusEnabled) + friendSectionControllerConnectivity = new FriendsSectionDoubleCollectionController(instantiatedView.FriendsSection, + friendsService, + friendEventBus, + mvcManager, + new FriendListPagedDoubleCollectionRequestManager(friendsService, friendEventBus, webRequestController, profileThumbnailCache, profileRepository, friendsConnectivityStatusTracker, instantiatedView.FriendsSection.LoopList, FRIENDS_PAGE_SIZE, FRIENDS_FETCH_ELEMENTS_THRESHOLD), + passportBridge, + profileThumbnailCache, + onlineUsersProvider, + realmNavigator, + systemClipboard, + friendsConnectivityStatusTracker, + includeUserBlocking); + else + friendSectionController = new FriendSectionController(instantiatedView.FriendsSection, + mvcManager, + systemClipboard, + new FriendListRequestManager(friendsService, friendEventBus, profileRepository, webRequestController, profileThumbnailCache, instantiatedView.FriendsSection.LoopList, FRIENDS_PAGE_SIZE, FRIENDS_FETCH_ELEMENTS_THRESHOLD), + passportBridge, + profileThumbnailCache, + onlineUsersProvider, + realmNavigator, + includeUserBlocking); + requestsSectionController = new RequestsSectionController(instantiatedView.RequestsSection, + friendsService, + friendEventBus, + mvcManager, + systemClipboard, + new RequestsRequestManager(friendsService, friendEventBus, webRequestController, profileThumbnailCache, FRIENDS_REQUEST_PAGE_SIZE, instantiatedView.RequestsSection.LoopList), + passportBridge, + profileThumbnailCache, + includeUserBlocking); + blockedSectionController = new BlockedSectionController(instantiatedView.BlockedSection, + web3IdentityCache, + new BlockedRequestManager(profileRepository, web3IdentityCache, webRequestController, profileThumbnailCache, FRIENDS_PAGE_SIZE, FRIENDS_FETCH_ELEMENTS_THRESHOLD), + passportBridge); + + requestsSectionController.ReceivedRequestsCountChanged += FriendRequestCountChanged; + sidebarActionsBus.SubscribeOnWidgetOpen(() => CloseFriendsPanel(default(InputAction.CallbackContext))); + } + + public override void Dispose() + { + base.Dispose(); + + viewInstance?.FriendsTabButton.onClick.RemoveAllListeners(); + viewInstance?.RequestsTabButton.onClick.RemoveAllListeners(); + viewInstance?.BlockedTabButton.onClick.RemoveAllListeners(); + viewInstance?.CloseButton.onClick.RemoveAllListeners(); + requestsSectionController.ReceivedRequestsCountChanged -= FriendRequestCountChanged; + friendsPanelCts.SafeCancelAndDispose(); + + blockedSectionController.Dispose(); + friendSectionController?.Dispose(); + friendSectionControllerConnectivity?.Dispose(); + requestsSectionController.Dispose(); + UnregisterCloseHotkey(); + } + + public UniTask InitAsync(CancellationToken ct) + { + var tasks = ListPool.Get(); + + try + { + tasks.Add(requestsSectionController.InitAsync(ct)); + + // TODO: Remove it after the server's connectivity stream works as expected + // This call forces to load at least the first page of friends (around 50) + // Currently, as a mid-term solution, we poll connectivity status for each of the friends we have asked to the server through the archipelago api + // If we do not request any friends then we will get no connectivity updates + // This approach will work for most of the users (except those who have more than 50 friends whose going to get partial updates) + // When server's rpc stream works, it will automatically send connectivity updates for each friend no matter if we previously asked for it or not + // if (friendSectionControllerConnectivity != null) + // tasks.Add(friendSectionControllerConnectivity.InitAsync(ct)); + + return UniTask.WhenAll(tasks); + } + finally + { + ListPool.Release(tasks); + } + } + + public void Reset() + { + requestsSectionController.Reset(); + friendSectionController?.Reset(); + friendSectionControllerConnectivity?.Reset(); + } + + private void RegisterCloseHotkey() + { + dclInput.Shortcuts.FriendPanel.performed += CloseFriendsPanel; + dclInput.UI.Close.performed += CloseFriendsPanel; + } + + private void UnregisterCloseHotkey() + { + dclInput.Shortcuts.FriendPanel.performed -= CloseFriendsPanel; + dclInput.UI.Close.performed -= CloseFriendsPanel; + } + + internal void CloseFriendsPanel(InputAction.CallbackContext obj) => + closeTaskCompletionSource.TrySetResult(); + + protected override void OnViewShow() => + RegisterCloseHotkey(); + + protected override void OnBeforeViewShow() + { + base.OnBeforeViewShow(); + friendsPanelCts = friendsPanelCts.SafeRestart(); + closeTaskCompletionSource = new UniTaskCompletionSource(); + + chatLifecycleBusController.HideChat(); + + ToggleTabs(inputData.TabToShow); + + sidebarActionsBus.CloseAllWidgets(); + } + + protected override void OnViewClose() + { + base.OnViewClose(); + + chatLifecycleBusController.ShowChat(); + + UnregisterCloseHotkey(); + } + + protected override void OnViewInstantiated() + { + base.OnViewInstantiated(); + + viewInstance!.FriendsTabButton.onClick.AddListener(() => ToggleTabs(FriendsPanelTab.FRIENDS)); + viewInstance.RequestsTabButton.onClick.AddListener(() => ToggleTabs(FriendsPanelTab.REQUESTS)); + viewInstance.BlockedTabButton.onClick.AddListener(() => ToggleTabs(FriendsPanelTab.BLOCKED)); + viewInstance.CloseButton.onClick.AddListener(() => CloseFriendsPanel(default(InputAction.CallbackContext))); + + viewInstance.BlockedTabButton.gameObject.SetActive(includeUserBlocking); + + ToggleTabs(FriendsPanelTab.FRIENDS); + } + + private void FriendRequestCountChanged(int count) + { + sidebarRequestNotificationIndicator.SetNotificationCount(count); + } + + internal void ToggleTabs(FriendsPanelTab tab) + { + viewInstance!.FriendsTabSelected.SetActive(tab == FriendsPanelTab.FRIENDS); + viewInstance!.FriendsSection.SetActive(tab == FriendsPanelTab.FRIENDS); + viewInstance.RequestsTabSelected.SetActive(tab == FriendsPanelTab.REQUESTS); + viewInstance.RequestsSection.SetActive(tab == FriendsPanelTab.REQUESTS); + viewInstance.BlockedTabSelected.SetActive(tab == FriendsPanelTab.BLOCKED); + viewInstance.BlockedSection.SetActive(tab == FriendsPanelTab.BLOCKED); + } + + protected override async UniTask WaitForCloseIntentAsync(CancellationToken ct) + { + await UniTask.WhenAny(viewInstance!.CloseButton.OnClickAsync(ct), viewInstance!.BackgroundCloseButton.OnClickAsync(ct), closeTaskCompletionSource.Task); + await HideViewAsync(ct); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelController.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelController.cs.meta new file mode 100644 index 0000000000..b350290f29 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2193768f47fd498c89cb723a23fd5b44 +timeCreated: 1736414183 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelParameter.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelParameter.cs new file mode 100644 index 0000000000..d90456219d --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelParameter.cs @@ -0,0 +1,13 @@ +namespace DCL.Friends.UI.FriendPanel +{ + public struct FriendsPanelParameter + { + public readonly FriendsPanelController.FriendsPanelTab TabToShow; + + public FriendsPanelParameter(FriendsPanelController.FriendsPanelTab tabToShow) + { + TabToShow = tabToShow; + } + + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelParameter.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelParameter.cs.meta new file mode 100644 index 0000000000..3830c4b7d2 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelParameter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9e686568608448389fb3e96a5be1f4ad +timeCreated: 1736414927 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelView.cs new file mode 100644 index 0000000000..8c9c0c39fb --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelView.cs @@ -0,0 +1,49 @@ +using Cysharp.Threading.Tasks; +using DCL.Audio; +using DCL.Friends.UI.FriendPanel.Sections.Blocked; +using DCL.Friends.UI.FriendPanel.Sections.Friends; +using DCL.Friends.UI.FriendPanel.Sections.Requests; +using MVC; +using System.Threading; +using UnityEngine; +using UnityEngine.UI; + +namespace DCL.Friends.UI.FriendPanel +{ + public class FriendsPanelView: ViewBaseWithAnimationElement, IView + { + [field: SerializeField] public Button CloseButton { get; private set; } + [field: SerializeField] public Button BackgroundCloseButton { get; private set; } + + [field: Header("Friends tab")] + [field: SerializeField] public Button FriendsTabButton { get; private set; } + [field: SerializeField] public GameObject FriendsTabSelected { get; private set; } + [field: SerializeField] public FriendsSectionView FriendsSection { get; private set; } + + [field: Header("Requests tab")] + [field: SerializeField] public Button RequestsTabButton { get; private set; } + [field: SerializeField] public GameObject RequestsTabSelected { get; private set; } + [field: SerializeField] public RequestsSectionView RequestsSection { get; private set; } + + [field: Header("Blocked tab")] + [field: SerializeField] public Button BlockedTabButton { get; private set; } + [field: SerializeField] public GameObject BlockedTabSelected { get; private set; } + [field: SerializeField] public BlockedSectionView BlockedSection { get; private set; } + + [field: Header("Audio")] + [field: SerializeField] public AudioClipConfig? OpenPanel { get; private set; } + [field: SerializeField] public AudioClipConfig? ClosePanel { get; private set; } + + protected override UniTask PlayShowAnimationAsync(CancellationToken ct) + { + UIAudioEventsBus.Instance.SendPlayAudioEvent(OpenPanel); + return base.PlayShowAnimationAsync(ct); + } + + protected override UniTask PlayHideAnimationAsync(CancellationToken ct) + { + UIAudioEventsBus.Instance.SendPlayAudioEvent(ClosePanel); + return base.PlayHideAnimationAsync(ct); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelView.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelView.cs.meta new file mode 100644 index 0000000000..88185804f1 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/FriendsPanelView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 26e50f2edc85447e96fcb896c52dfd87 +timeCreated: 1736414178 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/NotificationIndicatorView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/NotificationIndicatorView.cs new file mode 100644 index 0000000000..816b52b84d --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/NotificationIndicatorView.cs @@ -0,0 +1,18 @@ +using TMPro; +using UnityEngine; + +namespace DCL.Friends.UI.FriendPanel +{ + public class NotificationIndicatorView : MonoBehaviour + { + private const int MAX_NOTIFICATIONS_DISPLAYED = 9; + + [field: SerializeField] public TMP_Text NotificationText { get; private set; } + + public void SetNotificationCount(int count) + { + NotificationText.SetText(count > MAX_NOTIFICATIONS_DISPLAYED ? $"+{MAX_NOTIFICATIONS_DISPLAYED}" : $"{count}"); + gameObject.SetActive(count > 0); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/NotificationIndicatorView.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/NotificationIndicatorView.cs.meta new file mode 100644 index 0000000000..9878eb9a54 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/NotificationIndicatorView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c445fb9b7035494ba43c3fc672a920f3 +timeCreated: 1736424164 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/OnlineStatusConfiguration.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/OnlineStatusConfiguration.cs new file mode 100644 index 0000000000..56ab4ed54e --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/OnlineStatusConfiguration.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace DCL.Friends.UI.FriendPanel +{ + [CreateAssetMenu(fileName = "FriendPanelOnlineStatusSettings", menuName = "DCL/Friends/OnlineStatusConfiguration")] + [Serializable] + public class OnlineStatusConfiguration : ScriptableObject + { + [SerializeField] private List onlineStatusConfigurations; + + public OnlineStatusConfigurationData GetConfiguration(OnlineStatus status) + { + foreach (var config in onlineStatusConfigurations) + if (config.Status == status) + return config.Configuration; + + throw new ArgumentOutOfRangeException($"Couldn't find configuration for status {status}"); + } + } + + [Serializable] + public class StatusConfiguration + { + public OnlineStatus Status; + public OnlineStatusConfigurationData Configuration; + } + + [Serializable] + public class OnlineStatusConfigurationData + { + public Color StatusColor; + public string StatusText; + } + + public enum OnlineStatus + { + ONLINE, + AWAY, + OFFLINE + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/OnlineStatusConfiguration.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/OnlineStatusConfiguration.cs.meta new file mode 100644 index 0000000000..47c35560ad --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/OnlineStatusConfiguration.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6b3be250445c46feb68af2caf5e5ec25 +timeCreated: 1737106942 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerController.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerController.cs new file mode 100644 index 0000000000..24e342580c --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerController.cs @@ -0,0 +1,160 @@ +using Cysharp.Threading.Tasks; +using DCL.Friends.UI.FriendPanel.Sections; +using DCL.Friends.UI.Requests; +using DCL.NotificationsBusController.NotificationsBus; +using DCL.NotificationsBusController.NotificationTypes; +using DCL.Web3; +using MVC; +using System; +using System.Threading; +using UnityEngine.InputSystem; +using Utility; + +namespace DCL.Friends.UI.FriendPanel +{ + public class PersistentFriendPanelOpenerController : ControllerBase + { + private readonly IMVCManager mvcManager; + private readonly DCLInput dclInput; + private readonly IPassportBridge passportBridge; + private readonly IFriendsService friendsService; + + private FriendsPanelController? friendsPanelController; + private bool isFriendPanelControllerOpen; + private CancellationTokenSource? friendRequestReceivedCts; + + public override CanvasOrdering.SortingLayer Layer => CanvasOrdering.SortingLayer.Persistent; + + public PersistentFriendPanelOpenerController(ViewFactoryMethod viewFactory, + IMVCManager mvcManager, + DCLInput dclInput, + INotificationsBusController notificationsBusController, + IPassportBridge passportBridge, + IFriendsService friendsService) + : base(viewFactory) + { + this.mvcManager = mvcManager; + this.dclInput = dclInput; + this.passportBridge = passportBridge; + this.friendsService = friendsService; + + notificationsBusController.SubscribeToNotificationTypeClick(NotificationType.SOCIAL_SERVICE_FRIENDSHIP_REQUEST, FriendRequestReceived); + notificationsBusController.SubscribeToNotificationTypeClick(NotificationType.SOCIAL_SERVICE_FRIENDSHIP_ACCEPTED, FriendRequestAccepted); + mvcManager.OnViewShowed += OnViewShowed; + mvcManager.OnViewClosed += OnViewClosed; + RegisterHotkey(); + } + + public override void Dispose() + { + base.Dispose(); + + mvcManager.OnViewShowed -= OnViewShowed; + mvcManager.OnViewClosed -= OnViewClosed; + viewInstance?.OpenFriendPanelButton.onClick.RemoveListener(ToggleFriendsPanel); + UnregisterHotkey(); + friendRequestReceivedCts.SafeCancelAndDispose(); + } + + private void FriendRequestAccepted(object[] parameters) + { + if (parameters.Length == 0 || parameters[0] is not FriendRequestAcceptedNotification) + return; + + FriendRequestAcceptedNotification friendRequestAcceptedNotification = (FriendRequestAcceptedNotification)parameters[0]; + + passportBridge.ShowAsync(new Web3Address(friendRequestAcceptedNotification.Metadata.Sender.Address)).Forget(); + } + + private void FriendRequestReceived(object[] parameters) + { + if (parameters.Length == 0 || parameters[0] is not FriendRequestReceivedNotification) + return; + + friendRequestReceivedCts = friendRequestReceivedCts.SafeRestart(); + ManageFriendRequestReceivedNotificationAsync((FriendRequestReceivedNotification)parameters[0], friendRequestReceivedCts.Token).Forget(); + + async UniTaskVoid ManageFriendRequestReceivedNotificationAsync(FriendRequestReceivedNotification notification, CancellationToken ct) + { + FriendshipStatus friendshipStatus = await friendsService.GetFriendshipStatusAsync(notification.Metadata.Sender.Address, ct); + + switch (friendshipStatus) + { + case FriendshipStatus.FRIEND: + if (isFriendPanelControllerOpen) + friendsPanelController?.ToggleTabs(FriendsPanelController.FriendsPanelTab.FRIENDS); + else + ToggleFriendsPanel(); + break; + case FriendshipStatus.REQUEST_RECEIVED: + mvcManager.ShowAsync(FriendRequestController.IssueCommand(new FriendRequestParams + { + Request = new FriendRequest( + friendRequestId: notification.Metadata.RequestId, + timestamp: GetDateTimeFromString(notification.Timestamp), + from: notification.Metadata.Sender.ToFriendProfile(), + to: notification.Metadata.Receiver.ToFriendProfile(), + messageBody: notification.Metadata.Message) + }), ct).Forget(); + break; + default: + passportBridge.ShowAsync(new Web3Address(notification.Metadata.Sender.Address)).Forget(); + break; + } + } + } + + private DateTime GetDateTimeFromString(string epochString) => + !long.TryParse(epochString, out long unixTimestamp) ? new DateTime() : DateTimeOffset.FromUnixTimeMilliseconds(unixTimestamp).ToLocalTime().DateTime; + + private void RegisterHotkey() + { + dclInput.Shortcuts.FriendPanel.performed += OpenFriendsPanel; + } + + private void UnregisterHotkey() + { + dclInput.Shortcuts.FriendPanel.performed -= OpenFriendsPanel; + } + + protected override UniTask WaitForCloseIntentAsync(CancellationToken ct) => + UniTask.CompletedTask; + + protected override void OnViewInstantiated() + { + base.OnViewInstantiated(); + + viewInstance!.OpenFriendPanelButton.onClick.AddListener(ToggleFriendsPanel); + } + + private void OpenFriendsPanel(InputAction.CallbackContext obj) => + ToggleFriendsPanel(); + + private void ToggleFriendsPanel() + { + if (isFriendPanelControllerOpen) + friendsPanelController?.CloseFriendsPanel(default(InputAction.CallbackContext)); + else + mvcManager.ShowAsync(FriendsPanelController.IssueCommand(new FriendsPanelParameter())); + } + + private void OnViewShowed(IController controller) + { + if (controller is not FriendsPanelController friendsController) return; + + friendsPanelController ??= friendsController; + isFriendPanelControllerOpen = true; + viewInstance!.SetButtonStatePanelShow(true); + UnregisterHotkey(); + } + + private void OnViewClosed(IController controller) + { + if (controller is not FriendsPanelController) return; + + viewInstance!.SetButtonStatePanelShow(false); + isFriendPanelControllerOpen = false; + RegisterHotkey(); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerController.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerController.cs.meta new file mode 100644 index 0000000000..789a76e925 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7799d532a2184386951c581b187b7051 +timeCreated: 1737361849 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerView.cs new file mode 100644 index 0000000000..d6c31f4ccd --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerView.cs @@ -0,0 +1,24 @@ +using MVC; +using UnityEngine; +using UnityEngine.UI; + +namespace DCL.Friends.UI.FriendPanel +{ + public class PersistentFriendPanelOpenerView : ViewBase, IView + { + [field: SerializeField] + public Button OpenFriendPanelButton { get; private set; } + + [field: SerializeField] + public GameObject FriendsEnabledContainer { get; private set; } + + [field: SerializeField] + public GameObject FriendsDisabledContainer { get; private set; } + + public void SetButtonStatePanelShow(bool panelShown) + { + FriendsDisabledContainer.SetActive(!panelShown); + FriendsEnabledContainer.SetActive(panelShown); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerView.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerView.cs.meta new file mode 100644 index 0000000000..0071c9cc49 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/PersistentFriendPanelOpenerView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ec1c006ee7e24398b1c8859e8a4f6104 +timeCreated: 1737361736 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections.meta new file mode 100644 index 0000000000..d1dcc62143 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a5ccf2e3519842228a07c98c6a0bce61 +timeCreated: 1736852473 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked.meta new file mode 100644 index 0000000000..46ff3deab2 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7904c76a0378453f898f8b6e8213e526 +timeCreated: 1736852625 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedRequestManager.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedRequestManager.cs new file mode 100644 index 0000000000..6d51e65043 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedRequestManager.cs @@ -0,0 +1,57 @@ +using Cysharp.Threading.Tasks; +using DCL.Profiles; +using DCL.Web3.Identities; +using DCL.WebRequests; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace DCL.Friends.UI.FriendPanel.Sections.Blocked +{ + public class BlockedRequestManager : FriendPanelRequestManager + { + private readonly IProfileRepository profileRepository; + private readonly IWeb3IdentityCache web3IdentityCache; + + private FriendProfile? userProfile; + private List blockedProfiles = new (); + + public event Action? UnblockClicked; + public event Action? ContextMenuClicked; + + public BlockedRequestManager(IProfileRepository profileRepository, + IWeb3IdentityCache web3IdentityCache, + IWebRequestController webRequestController, + IProfileThumbnailCache profileThumbnailCache, + int pageSize, + int elementsMissingThreshold) : base(pageSize, elementsMissingThreshold, webRequestController, profileThumbnailCache) + { + this.profileRepository = profileRepository; + this.web3IdentityCache = web3IdentityCache; + } + + public override int GetCollectionCount() => + blockedProfiles.Count; + + protected override FriendProfile GetCollectionElement(int index) => + blockedProfiles[index]; + + protected override void CustomiseElement(BlockedUserView elementView, int index) + { + elementView.UnblockButton.onClick.RemoveAllListeners(); + elementView.UnblockButton.onClick.AddListener(() => UnblockClicked?.Invoke(elementView.UserProfile)); + + elementView.ContextMenuButton.onClick.RemoveAllListeners(); + elementView.ContextMenuButton.onClick.AddListener(() => ContextMenuClicked?.Invoke(elementView.UserProfile)); + } + + protected override async UniTask FetchDataAsync(int pageNumber, int pageSize, CancellationToken ct) + { + //TODO: Implement this with the new social service logic + return 0; + } + + protected override void ResetCollection() => + blockedProfiles.Clear(); + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedRequestManager.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedRequestManager.cs.meta new file mode 100644 index 0000000000..57cbd5e3a4 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedRequestManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 44e265fe3bb64d3396e39d7421de93a4 +timeCreated: 1737112135 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionController.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionController.cs new file mode 100644 index 0000000000..385f547617 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionController.cs @@ -0,0 +1,45 @@ +using Cysharp.Threading.Tasks; +using DCL.Diagnostics; +using DCL.Web3.Identities; +using MVC; +using UnityEngine; + +namespace DCL.Friends.UI.FriendPanel.Sections.Blocked +{ + public class BlockedSectionController : FriendPanelSectionController + { + private readonly IPassportBridge passportBridge; + + public BlockedSectionController(BlockedSectionView view, + IWeb3IdentityCache web3IdentityCache, + BlockedRequestManager requestManager, + IPassportBridge passportBridge) : base(view, requestManager) + { + this.passportBridge = passportBridge; + + requestManager.UnblockClicked += UnblockUserClicked; + requestManager.ContextMenuClicked += ContextMenuClicked; + } + + public override void Dispose() + { + base.Dispose(); + requestManager.UnblockClicked -= UnblockUserClicked; + requestManager.ContextMenuClicked -= ContextMenuClicked; + } + + private void UnblockUserClicked(FriendProfile profile) + { + ReportHub.Log(LogType.Error, new ReportData(ReportCategory.FRIENDS), $"Unblock user button clicked for {profile.Address.ToString()}. Users should not be able to reach this"); + } + + private void ContextMenuClicked(FriendProfile profile) + { + ReportHub.Log(LogType.Error, new ReportData(ReportCategory.FRIENDS), $"Context menu on blocked user button clicked for {profile.Address.ToString()}. Users should not be able to reach this"); + } + + protected override void ElementClicked(FriendProfile profile) => + passportBridge.ShowAsync(profile.Address).Forget(); + + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionController.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionController.cs.meta new file mode 100644 index 0000000000..8b82af6fb9 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d1f49d1d916f4ea7990cf2d6ca88723f +timeCreated: 1736756769 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionView.cs new file mode 100644 index 0000000000..2893d4976f --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionView.cs @@ -0,0 +1,6 @@ +namespace DCL.Friends.UI.FriendPanel.Sections.Blocked +{ + public class BlockedSectionView : FriendPanelSectionView + { + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionView.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionView.cs.meta new file mode 100644 index 0000000000..1ccfdb9a62 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedSectionView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 59f00d19232d4bdaad5ac96e7fbabc71 +timeCreated: 1736756689 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedUserView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedUserView.cs new file mode 100644 index 0000000000..d5e2afdd1c --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedUserView.cs @@ -0,0 +1,41 @@ +using DCL.WebRequests; +using System; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace DCL.Friends.UI.FriendPanel.Sections.Blocked +{ + public class BlockedUserView : FriendPanelUserView + { + [field: SerializeField] public Button UnblockButton { get; private set; } + [field: SerializeField] public Button ContextMenuButton { get; private set; } + [field: SerializeField] public TMP_Text BlockedDateText { get; private set; } + + private DateTime blockedDate; + public DateTime BlockedDate + { + get => blockedDate; + + set + { + blockedDate = value; + BlockedDateText.SetText($"{blockedDate:MM/dd}"); + } + } + + public override void Configure(FriendProfile profile, IWebRequestController webRequestController, IProfileThumbnailCache profileThumbnailCache) + { + buttons.Clear(); + buttons.Add(UnblockButton); + buttons.Add(ContextMenuButton); + base.Configure(profile, webRequestController, profileThumbnailCache); + } + + protected override void ToggleButtonView(bool isActive) + { + base.ToggleButtonView(isActive); + BlockedDateText.gameObject.SetActive(!isActive); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedUserView.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedUserView.cs.meta new file mode 100644 index 0000000000..21b416133e --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/Blocked/BlockedUserView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1715c1e39b9e42cc815cc9763bb643ca +timeCreated: 1736764931 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelDoubleCollectionRequestManager.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelDoubleCollectionRequestManager.cs new file mode 100644 index 0000000000..68dbe08bba --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelDoubleCollectionRequestManager.cs @@ -0,0 +1,220 @@ +using Cysharp.Threading.Tasks; +using DCL.WebRequests; +using SuperScrollView; +using System; +using System.Threading; +using Utility; + +namespace DCL.Friends.UI.FriendPanel.Sections +{ + public abstract class FriendPanelDoubleCollectionRequestManager : IDisposable where T : FriendPanelUserView + { + protected readonly IFriendsService friendsService; + protected readonly IFriendsEventBus friendEventBus; + private readonly int pageSize; + private readonly int elementsMissingThreshold; + private readonly bool disablePagination; + + private readonly IWebRequestController webRequestController; + private readonly IProfileThumbnailCache profileThumbnailCache; + private readonly FriendPanelStatus firstCollectionStatus; + private readonly FriendPanelStatus secondCollectionStatus; + private readonly int statusElementIndex; + private readonly int emptyElementIndex; + private readonly int userElementIndex; + private readonly CancellationTokenSource fetchNewDataCts = new (); + + private int pageNumber; + private int totalFetched; + private int totalToFetch; + private bool isFetching; + + private bool excludeFirstCollection; + private bool excludeSecondCollection; + + public bool HasElements { get; private set; } + public bool WasInitialised { get; private set; } + + public event Action? ElementClicked; + public event Action? FirstFolderClicked; + public event Action? SecondFolderClicked; + + protected FriendPanelDoubleCollectionRequestManager(IFriendsService friendsService, + IFriendsEventBus friendEventBus, + IWebRequestController webRequestController, + IProfileThumbnailCache profileThumbnailCache, + int pageSize, + int elementsMissingThreshold, + FriendPanelStatus firstCollectionStatus, + FriendPanelStatus secondCollectionStatus, + int statusElementIndex, + int emptyElementIndex, + int userElementIndex, + bool disablePagination = false) + { + this.friendsService = friendsService; + this.friendEventBus = friendEventBus; + this.webRequestController = webRequestController; + this.profileThumbnailCache = profileThumbnailCache; + this.pageSize = pageSize; + this.elementsMissingThreshold = elementsMissingThreshold; + this.firstCollectionStatus = firstCollectionStatus; + this.secondCollectionStatus = secondCollectionStatus; + this.statusElementIndex = statusElementIndex; + this.emptyElementIndex = emptyElementIndex; + this.userElementIndex = userElementIndex; + this.disablePagination = disablePagination; + } + + public virtual void Dispose() + { + fetchNewDataCts.SafeCancelAndDispose(); + } + + protected abstract int GetFirstCollectionCount(); + protected abstract int GetSecondCollectionCount(); + + protected abstract FriendProfile GetFirstCollectionElement(int index); + protected abstract FriendProfile GetSecondCollectionElement(int index); + + protected virtual void CustomiseElement(T elementView, int index, FriendPanelStatus section) { } + + public LoopListViewItem2 GetLoopListItemByIndex(LoopListView2 loopListView, int index) + { + LoopListViewItem2 listItem = null; + int onlineFriendMarker = excludeFirstCollection ? 0 : GetFirstCollectionCount(); + if (GetFirstCollectionCount() == 0 && !excludeFirstCollection) onlineFriendMarker++; //Count the empty element + int offlineFriendMarker = excludeSecondCollection ? 0 : GetSecondCollectionCount(); + if (GetSecondCollectionCount() == 0 && !excludeSecondCollection) offlineFriendMarker++; //Count the empty element + + if (index == 0) + { + listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[statusElementIndex].mItemPrefab.name); + StatusWrapperView statusWrapperView = listItem.GetComponent(); + statusWrapperView.SetStatusText(firstCollectionStatus, GetFirstCollectionCount()); + statusWrapperView.ResetCallback(); + statusWrapperView.FolderButtonClicked += FolderClick; + } + else if (index > 0 && index <= onlineFriendMarker) + { + if (GetFirstCollectionCount() == 0 && !excludeFirstCollection) + listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[emptyElementIndex].mItemPrefab.name); + else + { + listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[userElementIndex].mItemPrefab.name); + T friendListUserView = listItem.GetComponent(); + int collectionIndex = index - 1; + friendListUserView.Configure(GetFirstCollectionElement(collectionIndex), webRequestController, profileThumbnailCache); + CustomiseElement(friendListUserView, collectionIndex, firstCollectionStatus); + friendListUserView.RemoveMainButtonClickListeners(); + friendListUserView.MainButtonClicked += profile => ElementClicked?.Invoke(profile); + friendListUserView.RemoveSpriteLoadedListeners(); + friendListUserView.SpriteLoaded += sprite => profileThumbnailCache.SetThumbnail(friendListUserView.UserProfile.Address.ToString(), sprite); + } + } + else if (index == onlineFriendMarker + 1) + { + listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[statusElementIndex].mItemPrefab.name); + StatusWrapperView statusWrapperView = listItem.GetComponent(); + statusWrapperView.SetStatusText(secondCollectionStatus, GetSecondCollectionCount()); + statusWrapperView.ResetCallback(); + statusWrapperView.FolderButtonClicked += FolderClick; + } + else if (index > onlineFriendMarker + 1 && index <= onlineFriendMarker + 1 + offlineFriendMarker + 1) + { + if (GetSecondCollectionCount() == 0 && !excludeSecondCollection) + listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[emptyElementIndex].mItemPrefab.name); + else + { + listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[userElementIndex].mItemPrefab.name); + T friendListUserView = listItem.GetComponent(); + int collectionIndex = index - onlineFriendMarker - 2; + friendListUserView.Configure(GetSecondCollectionElement(collectionIndex), webRequestController, profileThumbnailCache); + CustomiseElement(friendListUserView, collectionIndex, secondCollectionStatus); + friendListUserView.RemoveMainButtonClickListeners(); + friendListUserView.MainButtonClicked += profile => ElementClicked?.Invoke(profile); + friendListUserView.RemoveSpriteLoadedListeners(); + friendListUserView.SpriteLoaded += sprite => profileThumbnailCache.SetThumbnail(friendListUserView.UserProfile.Address.ToString(), sprite); + + if (!disablePagination && collectionIndex >= GetSecondCollectionCount() - elementsMissingThreshold && totalFetched < totalToFetch && !isFetching) + FetchNewDataAsync(loopListView, fetchNewDataCts.Token).Forget(); + } + } + + return listItem; + } + + private async UniTaskVoid FetchNewDataAsync(LoopListView2 loopListView, CancellationToken ct) + { + isFetching = true; + + pageNumber++; + await FetchDataInternalAsync(ct); + + loopListView.SetListItemCount(GetElementsNumber(), false); + loopListView.RefreshAllShownItem(); + + isFetching = false; + } + + private async UniTask FetchDataInternalAsync(CancellationToken ct) + { + totalToFetch = await FetchDataAsync(pageNumber, pageSize, ct); + totalFetched = (pageNumber + 1) * pageSize; + } + + public int GetElementsNumber() + { + int count = 2; + + if (!excludeFirstCollection) + count += GetFirstCollectionCount(); + + if (!excludeSecondCollection) + count += GetSecondCollectionCount(); + + if (GetFirstCollectionCount() == 0 && !excludeFirstCollection) + count++; + + if (GetSecondCollectionCount() == 0 && !excludeSecondCollection) + count++; + + return count; + } + + public void Reset() + { + HasElements = false; + WasInitialised = false; + pageNumber = 0; + totalFetched = 0; + ResetCollections(); + } + + protected abstract void ResetCollections(); + + protected abstract UniTask FetchDataAsync(int pageNumber, int pageSize, CancellationToken ct); + + public async UniTask InitAsync(CancellationToken ct) + { + await FetchDataInternalAsync(ct); + + HasElements = GetFirstCollectionCount() + GetSecondCollectionCount() > 0; + WasInitialised = true; + } + + private void FolderClick(bool isFolded, FriendPanelStatus panelStatus) + { + if (panelStatus == firstCollectionStatus) + { + excludeFirstCollection = isFolded; + FirstFolderClicked?.Invoke(); + } + else if (panelStatus == secondCollectionStatus) + { + excludeSecondCollection = isFolded; + SecondFolderClicked?.Invoke(); + } + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelDoubleCollectionRequestManager.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelDoubleCollectionRequestManager.cs.meta new file mode 100644 index 0000000000..69061bdb26 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelDoubleCollectionRequestManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 99ebecec1ab34453b110dcc2a6921679 +timeCreated: 1736951152 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelRequestManager.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelRequestManager.cs new file mode 100644 index 0000000000..4c1ded74f7 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelRequestManager.cs @@ -0,0 +1,108 @@ +using Cysharp.Threading.Tasks; +using DCL.WebRequests; +using SuperScrollView; +using System; +using System.Threading; +using Utility; + +namespace DCL.Friends.UI.FriendPanel.Sections +{ + public abstract class FriendPanelRequestManager : IDisposable where T : FriendPanelUserView + { + private readonly int pageSize; + private readonly int elementsMissingThreshold; + private readonly IWebRequestController webRequestController; + private readonly IProfileThumbnailCache profileThumbnailCache; + private readonly CancellationTokenSource fetchNewDataCts = new (); + + private int pageNumber; + private int totalFetched; + private int totalToFetch; + private bool isFetching; + + public bool HasElements { get; private set; } + public bool WasInitialised { get; private set; } + + public event Action? ElementClicked; + + protected FriendPanelRequestManager(int pageSize, int elementsMissingThreshold, + IWebRequestController webRequestController, + IProfileThumbnailCache profileThumbnailCache) + { + this.pageSize = pageSize; + this.elementsMissingThreshold = elementsMissingThreshold; + this.webRequestController = webRequestController; + this.profileThumbnailCache = profileThumbnailCache; + } + + public virtual void Dispose() + { + fetchNewDataCts.SafeCancelAndDispose(); + } + + public abstract int GetCollectionCount(); + protected abstract FriendProfile GetCollectionElement(int index); + + protected virtual void CustomiseElement(T elementView, int index) { } + + public LoopListViewItem2 GetLoopListItemByIndex(LoopListView2 loopListView, int index) + { + LoopListViewItem2 listItem = loopListView.NewListViewItem(loopListView.ItemPrefabDataList[0].mItemPrefab.name); + T view = listItem.GetComponent(); + view.Configure(GetCollectionElement(index), webRequestController, profileThumbnailCache); + + view.RemoveMainButtonClickListeners(); + view.MainButtonClicked += profile => ElementClicked?.Invoke(profile); + + view.RemoveSpriteLoadedListeners(); + view.SpriteLoaded += sprite => profileThumbnailCache.SetThumbnail(view.UserProfile.Address.ToString(), sprite); + + CustomiseElement(view, index); + + if (index >= totalFetched - elementsMissingThreshold && totalFetched < totalToFetch && !isFetching) + FetchNewDataAsync(loopListView, fetchNewDataCts.Token).Forget(); + + return listItem; + } + + private async UniTaskVoid FetchNewDataAsync(LoopListView2 loopListView, CancellationToken ct) + { + isFetching = true; + + pageNumber++; + await FetchDataInternalAsync(ct); + + loopListView.SetListItemCount(GetCollectionCount(), false); + loopListView.RefreshAllShownItem(); + + isFetching = false; + } + + private async UniTask FetchDataInternalAsync(CancellationToken ct) + { + totalToFetch = await FetchDataAsync(pageNumber, pageSize, ct); + totalFetched = (pageNumber + 1) * pageSize; + } + + protected abstract UniTask FetchDataAsync(int pageNumber, int pageSize, CancellationToken ct); + + public async UniTask InitAsync(CancellationToken ct) + { + await FetchDataInternalAsync(ct); + + HasElements = GetCollectionCount() > 0; + WasInitialised = true; + } + + public void Reset() + { + HasElements = false; + WasInitialised = false; + pageNumber = 0; + totalFetched = 0; + ResetCollection(); + } + + protected abstract void ResetCollection(); + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelRequestManager.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelRequestManager.cs.meta new file mode 100644 index 0000000000..c152ba0dc9 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelRequestManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9e2d8d15b4e14338a59fd9c17195acec +timeCreated: 1737371035 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionController.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionController.cs new file mode 100644 index 0000000000..2a07025473 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionController.cs @@ -0,0 +1,85 @@ +using Cysharp.Threading.Tasks; +using SuperScrollView; +using System; +using System.Threading; +using Utility; + +namespace DCL.Friends.UI.FriendPanel.Sections +{ + public abstract class FriendPanelSectionController : IDisposable + where T : FriendPanelSectionView + where K : FriendPanelUserView + where U : FriendPanelRequestManager + { + protected readonly T view; + protected readonly U requestManager; + + protected UniTaskCompletionSource? panelLifecycleTask; + private CancellationTokenSource friendListInitCts = new (); + + protected FriendPanelSectionController(T view, + U requestManager) + { + this.view = view; + this.requestManager = requestManager; + + this.view.Enable += Enable; + this.view.Disable += Disable; + this.view.LoopList.InitListView(0, OnGetItemByIndex); + requestManager.ElementClicked += ElementClicked; + } + + public virtual void Dispose() + { + view.Enable -= Enable; + view.Disable -= Disable; + requestManager.ElementClicked -= ElementClicked; + requestManager.Dispose(); + friendListInitCts.SafeCancelAndDispose(); + } + + public void Reset() => + requestManager.Reset(); + + private LoopListViewItem2 OnGetItemByIndex(LoopListView2 loopListView, int index) => + requestManager.GetLoopListItemByIndex(loopListView, index); + + protected abstract void ElementClicked(FriendProfile profile); + + private void Enable() + { + view.LoopList.ResetListView(); + panelLifecycleTask = new UniTaskCompletionSource(); + if (!requestManager.WasInitialised) + InitAsync(friendListInitCts.Token).Forget(); + } + + private void Disable() + { + panelLifecycleTask?.TrySetResult(); + friendListInitCts = friendListInitCts.SafeRestart(); + } + + private void RefreshLoopList() + { + view.LoopList.SetListItemCount(requestManager.GetCollectionCount(), false); + view.LoopList.RefreshAllShownItem(); + } + + protected virtual async UniTaskVoid InitAsync(CancellationToken ct) + { + view.SetLoadingState(true); + view.SetEmptyState(false); + view.SetScrollViewState(false); + + await requestManager.InitAsync(ct); + + view.SetLoadingState(false); + view.SetEmptyState(!requestManager.HasElements); + view.SetScrollViewState(requestManager.HasElements); + + if (requestManager.HasElements) + RefreshLoopList(); + } + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionController.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionController.cs.meta new file mode 100644 index 0000000000..05ec2bc30e --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 30f935936df845738b3eb929748d5e02 +timeCreated: 1737371964 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionDoubleCollectionController.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionDoubleCollectionController.cs new file mode 100644 index 0000000000..60b0463d05 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionDoubleCollectionController.cs @@ -0,0 +1,115 @@ +using Cysharp.Threading.Tasks; +using MVC; +using SuperScrollView; +using System; +using System.Threading; +using Utility; + +namespace DCL.Friends.UI.FriendPanel.Sections +{ + public abstract class FriendPanelSectionDoubleCollectionController : IDisposable + where T : FriendPanelSectionView + where K : FriendPanelUserView + where U : FriendPanelDoubleCollectionRequestManager + { + protected readonly T view; + protected readonly IFriendsService friendsService; + protected readonly IFriendsEventBus friendEventBus; + protected readonly IMVCManager mvcManager; + protected readonly U requestManager; + + protected UniTaskCompletionSource? panelLifecycleTask; + private CancellationTokenSource friendListInitCts = new (); + + protected FriendPanelSectionDoubleCollectionController(T view, + IFriendsService friendsService, + IFriendsEventBus friendEventBus, + IMVCManager mvcManager, + U requestManager) + { + this.view = view; + this.friendsService = friendsService; + this.friendEventBus = friendEventBus; + this.mvcManager = mvcManager; + this.requestManager = requestManager; + + this.view.Enable += Enable; + this.view.Disable += Disable; + this.view.LoopList.InitListView(0, OnGetItemByIndex); + requestManager.ElementClicked += ElementClicked; + } + + public virtual void Dispose() + { + view.Enable -= Enable; + view.Disable -= Disable; + requestManager.Dispose(); + friendListInitCts.SafeCancelAndDispose(); + requestManager.FirstFolderClicked -= FolderClicked; + requestManager.SecondFolderClicked -= FolderClicked; + requestManager.ElementClicked -= ElementClicked; + } + + public virtual void Reset() + { + requestManager.Reset(); + requestManager.FirstFolderClicked -= FolderClicked; + requestManager.SecondFolderClicked -= FolderClicked; + } + + protected void CheckShouldInit() + { + if (!requestManager.WasInitialised) + InitAsync(friendListInitCts.Token).Forget(); + } + + private void Enable() + { + view.LoopList.ResetListView(); + panelLifecycleTask = new UniTaskCompletionSource(); + CheckShouldInit(); + } + + private void Disable() + { + panelLifecycleTask?.TrySetResult(); + friendListInitCts = friendListInitCts.SafeRestart(); + } + + protected void FolderClicked() + { + RefreshLoopList(); + } + + protected void RefreshLoopList() + { + view.LoopList.SetListItemCount(requestManager.GetElementsNumber(), false); + view.LoopList.RefreshAllShownItem(); + } + + public virtual async UniTask InitAsync(CancellationToken ct) + { + view.SetLoadingState(true); + view.SetEmptyState(false); + view.SetScrollViewState(false); + + await requestManager.InitAsync(ct); + + view.SetLoadingState(false); + view.SetEmptyState(!requestManager.HasElements); + view.SetScrollViewState(requestManager.HasElements); + + if (requestManager.HasElements) + { + RefreshLoopList(); + requestManager.FirstFolderClicked += FolderClicked; + requestManager.SecondFolderClicked += FolderClicked; + } + } + + private LoopListViewItem2 OnGetItemByIndex(LoopListView2 loopListView, int index) => + requestManager.GetLoopListItemByIndex(loopListView, index); + + protected abstract void ElementClicked(FriendProfile profile); + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionDoubleCollectionController.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionDoubleCollectionController.cs.meta new file mode 100644 index 0000000000..ffdf8327e4 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionDoubleCollectionController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 371c41bcccc14b95b3ecd18b161cf8ef +timeCreated: 1737036157 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionView.cs new file mode 100644 index 0000000000..5cc326a7af --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionView.cs @@ -0,0 +1,39 @@ +using SuperScrollView; +using System; +using UnityEngine; + +namespace DCL.Friends.UI.FriendPanel.Sections +{ + public class FriendPanelSectionView : MonoBehaviour + { + [field: SerializeField] public LoopListView2 LoopList { get; private set; } + [field: SerializeField] public SectionLoadingView LoadingObject { get; private set; } + [field: SerializeField] public GameObject? EmptyState { get; private set; } + + public event Action Enable; + public event Action Disable; + + public void SetActive(bool isActive) => + gameObject.SetActive(isActive); + + private void OnEnable() => + Enable?.Invoke(); + + private void OnDisable() => + Disable?.Invoke(); + + public void SetLoadingState(bool isLoading) + { + if (isLoading) + LoadingObject?.Show(); + else + LoadingObject?.Hide(); + } + + public void SetEmptyState(bool isEmpty) => + EmptyState?.SetActive(isEmpty); + + public void SetScrollViewState(bool active) => + LoopList?.gameObject.SetActive(active); + } +} diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionView.cs.meta b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionView.cs.meta new file mode 100644 index 0000000000..bf548f84be --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelSectionView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 06def17f2b884ed79be5afc78a3c59d4 +timeCreated: 1736783770 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelUserView.cs b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelUserView.cs new file mode 100644 index 0000000000..27678fe450 --- /dev/null +++ b/Explorer/Assets/DCL/Friends/UI/FriendPanel/Sections/FriendPanelUserView.cs @@ -0,0 +1,128 @@ +using CommunicationData.URLHelpers; +using DCL.Chat; +using DCL.UI; +using DCL.WebRequests; +using System; +using System.Collections.Generic; +using TMPro; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace DCL.Friends.UI.FriendPanel.Sections +{ + public class FriendPanelUserView : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler + { + protected readonly List