Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- declaring variables: services, the player itself, gui elements, debounce and remote functions
- local players = game:GetService('Players')
- local replicatedStorage = game:GetService('ReplicatedStorage')
- local player = players.LocalPlayer
- local mouse = player:GetMouse()
- -- gui elements
- local gui = player.PlayerGui:WaitForChild('ScreenGui')
- local menu = gui:WaitForChild('menu')
- local new = gui:WaitForChild('new')
- local view = gui:WaitForChild('view')
- local modify = gui:WaitForChild('modify')
- local loadingImage = gui:WaitForChild('loading')
- local mDataFrame = modify.DataFrame
- local vDataFrame = view.DataFrame
- -- debounces
- local debounces = {}
- -- remote functions
- local sendRemote = replicatedStorage.sendSuggestion
- local viewRemote = replicatedStorage.viewSuggestions
- local viewAllRemote = replicatedStorage.viewAllSuggestions
- local modifyRemote = replicatedStorage.modifySuggestion
- --[[loading function:
- on function initiation, creates a loading image that follows the mouse around and returns the image itself
- the initiator can later on delete the image that was returned in order to stop the loading
- ]]
- local function newLoad()
- local image = loadingImage:Clone()
- image.Parent = gui
- task.spawn(function()
- while task.wait() do
- if not image then
- break
- end
- image.Rotation += 2
- image.Position = UDim2.new(0, mouse.X, 0, mouse.Y)
- end
- end)
- return image
- end
- --functionality of gui buttons: switch between frames (menu/view/new/modify), return to the menu from any
- --subframe
- local function switch(from, to)
- from.Visible = false
- to.Visible = true
- end
- menu.new.MouseButton1Click:Connect(function()
- switch(menu, new)
- end)
- menu.view.MouseButton1Click:Connect(function()
- switch(menu, view)
- end)
- menu.modify.MouseButton1Click:Connect(function()
- switch(menu, modify)
- end)
- new.back.MouseButton1Click:Connect(function()
- switch(new, menu)
- end)
- view.back.MouseButton1Click:Connect(function()
- switch(view, menu)
- end)
- modify.back.MouseButton1Click:Connect(function()
- switch(modify, menu)
- end)
- vDataFrame.back.MouseButton1Click:Connect(function()
- switch(vDataFrame, view.ScrollingFrame)
- end)
- mDataFrame.back.MouseButton1Click:Connect(function()
- switch(mDataFrame, modify.ScrollingFrame)
- end)
- -- send suggestion functionality
- local function validateTextInput(text)
- --[[
- ensure that the text is not empty or exceeds the character limit. if the text's length is less than 10,
- return false and changes the text's color. if the text's length exceeds 512, return false either
- otherwise, return true
- providing clear and immediate feedback on validation results enhances usability by allowing users to quickly
- understand and correct issues with their input
- --]]
- if #text < 10 then
- new.note.Text = "Text is too short!"
- new.note.TextColor3 = Color3.new(1, 0, 0)
- return false
- elseif #text > 512 then
- new.note.Text = "Text is too long!"
- new.note.TextColor3 = Color3.new(1, 0, 0)
- return false
- end
- return true
- end
- -- function to handle the response after suggestion submission
- local function handleSuggestionResponse(response)
- --[[
- handle the server's response after submitting a suggestion
- 1. display success or failure message
- 2. reset the textbox and button after a delay of 60 seconds (aka debounce)
- visual feedback ensures users are informed about the outcome of their submission, reinforcing trust
- in the system and the delay prevents spamming and preserves server integrity
- --]]
- if response.success then
- new.note.Text = "Suggestion submitted successfully!"
- new.note.TextColor3 = Color3.new(0, 1, 0)
- else
- new.note.Text = "Failed to submit suggestion!"
- new.note.TextColor3 = Color3.new(1, 0, 0)
- end
- task.wait(60)
- new.write.Interactable = true
- end
- new.send.MouseButton1Click:Connect(function()
- -- a debounce and text input validation checks, to avoid spam clicks and invalid text
- if not validateTextInput(new.write.Text) then
- return
- end
- if debounces['sendDebounce'] then
- return
- end
- debounces.sendDebounce = true
- --[[
- sends suggestions to the server and returns whether it was successful or not
- main functionality:
- 1. send a request to the server with the text from the textbox
- 2. store the server's response and reflect the success or failure visually on note's text color
- 3. disable the send button while processing the request
- 4. display the server's response briefly, then reset the gui elements
- providing visual cues, such as disabling the button, prevents user confusion and unintentional duplicate
- submissions
- --]]
- local image = newLoad()
- local response = sendRemote:InvokeServer(new.write.Text)
- image:Destroy()
- handleSuggestionResponse(response)
- debounces.sendDebounce = nil
- end)
- -- dynamically updating text length status in the textbox
- new.write:GetPropertyChangedSignal('Text'):Connect(function()
- --[[
- updates the note's text to show the number of characters entered so far,
- and changes color based on length validation
- by dynamically showing the text length, users receive immediate feedback, helping them stay within limits
- without trial and error
- --]]
- new.note.Text = #new.write.Text..'/512' -- displays text length
- new.note.TextColor3 = (#new.write.Text < 512 and #new.write.Text > 10) and Color3.new(0, 1) or Color3.new(1)
- end)
- local function updateStatus(statusLabel, status)
- --[[
- update the visual status of suggestions
- status labels' color changes depending on the "status" variable:
- 0 - in view (yellow)
- 1 - approved (green)
- 2 - rejected (red)
- visually distinguishing statuses with colors simplifies understanding for users at a glance, improving
- accessibility and clarity
- --]]
- statusLabel.TextColor3 =
- status == 0 and Color3.new(1, 1)
- or status == 1 and Color3.new(0, 1)
- or Color3.new(1)
- statusLabel.Text =
- status == 0 and 'In view'
- or status == 1 and 'Approved'
- or 'Rejected'
- end
- local function updateViewList()
- --[[
- shows a list of your suggestions with its details including: text, status, # of the suggestion
- functionality:
- 1. clear previous content in the scrolling frame
- 2. fetch suggestions from the server
- 3. if successful, populate the frame with buttons for each suggestion
- 4. clicking a button reveals detailed data about the suggestion
- this systematic approach ensures users can navigate and interact with their data intuitively, even when
- the list grows large
- --]]
- for _, frame in next, view.ScrollingFrame:GetChildren() do
- if frame.Name ~= 'UIGridLayout' and frame.Name ~= 'template' then
- frame:Destroy()
- end
- end
- local response = viewRemote:InvokeServer()
- if not response then
- return
- end
- for id, data in next, response do
- local template = view.ScrollingFrame.template:Clone()
- template.Parent = view.ScrollingFrame
- template.Visible = true
- template.Name = id
- template.suggestion.Text = string.format('Suggestion #%s', id)
- template.suggestion.TextColor3 =
- data.status == 0 and Color3.new(1, 1)
- or data.status == 1 and Color3.new(0, 1)
- or Color3.new(1)
- template.suggestion.MouseButton1Click:Connect(function()
- vDataFrame.text.Text = data.text
- vDataFrame.TextLabel.Text = string.format('Suggestion #%s', id)
- updateStatus(vDataFrame.status, data.status)
- vDataFrame.Visible = true
- view.ScrollingFrame.Visible = false
- end)
- end
- end
- updateViewList()
- local function updateModifyList()
- --[[
- functionality is identical to updateViewList, but includes player-specific data, showing
- which player made the suggestion.
- including player details ensures moderators can manage and review suggestions more effectively, increasing
- accountability
- --]]
- for _, frame in next, modify.ScrollingFrame:GetChildren() do
- if frame.Name ~= 'UIGridLayout' and frame.Name ~= 'template' then
- frame:Destroy()
- end
- end
- local response = viewAllRemote:InvokeServer()
- if not response then
- return
- end
- for playerId, playerData in next, response do
- local playerName = players:GetNameFromUserIdAsync(playerId)
- for id, data in next, playerData do
- local template = modify.ScrollingFrame.template:Clone()
- template.Parent = modify.ScrollingFrame
- template.Visible = true
- template.Name = id
- template.suggestion.Text = string.format(`Suggestion #%s (by @%s)`, id, playerName)
- template.suggestion.TextColor3 =
- data.status == 0 and Color3.new(1, 1)
- or data.status == 1 and Color3.new(0, 1)
- or Color3.new(1)
- template.suggestion.MouseButton1Click:Connect(function()
- mDataFrame.text.Text = data.text
- mDataFrame.TextLabel.Text = string.format('Suggestion #%s (by @%s)', id, playerName)
- updateStatus(mDataFrame.status, data.status)
- mDataFrame.status.MouseButton1Click:Connect(function()
- if debounces['statusDebounce'] then
- return
- end
- debounces.statusDebounce = true
- --[[
- the status cycle represents the different states of a suggestion:
- 0: in view (no decision has been made)
- 1: accepted (the suggestion has been accepted)
- 2: rejected (the suggestion has been rejected)
- the status is incremented each time, and after reaching 2, it wraps back to 0
- providing this cyclic behavior allows for easier toggling between states, simplifying
- moderation
- --]]
- local image = newLoad()
- local newStatus = (data.status + 1) % 3
- local response = modifyRemote:InvokeServer(playerId, id, newStatus)
- image:Destroy()
- debounces.statusDebounce = nil
- if not response then
- return
- end
- data.status = response
- updateStatus(mDataFrame.status, response)
- template.suggestion.TextColor3 =
- response == 0 and Color3.new(1, 1)
- or response == 1 and Color3.new(0, 1)
- or Color3.new(1)
- end)
- mDataFrame.Visible = true
- modify.ScrollingFrame.Visible = false
- end)
- end
- end
- end
- updateModifyList()
- local function updateButton(update, name)
- --[[
- add functionality to the update buttons
- 1. initiate the corresponding update function (updateViewList or updateModifyList)
- 2. rotate the button while the update is in progress
- 3. prevent multiple simultaneous updates using debounces
- this visual rotation effect reinforces that an update is ongoing, reducing user uncertainty during longer
- operations
- --]]
- update.MouseButton1Click:Connect(function()
- if debounces[name] then
- return
- end
- debounces[name] = true
- local respondReceived = false
- coroutine.wrap(function()
- repeat
- gui[name].update.Rotation += 2
- task.wait()
- until respondReceived
- gui[name].update.Rotation = 0
- end)()
- local image = newLoad()
- if name == 'view' then
- updateViewList()
- else
- updateModifyList()
- end
- image:Destroy()
- respondReceived = true
- task.wait(3)
- debounces[name] = false
- end)
- end
- updateButton(view.update, 'view')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement