Improve displayed info about third party plugins

This commit is contained in:
boatbomber 2024-12-22 15:18:29 -08:00
parent 12e3d2b1a5
commit 139b283919
7 changed files with 217 additions and 58 deletions

View file

@ -12,6 +12,7 @@ local Theme = require(Plugin.App.Theme)
local Assets = require(Plugin.Assets)
local bindingUtil = require(Plugin.App.bindingUtil)
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
local getThirdPartyIcon = require(Plugin.getThirdPartyIcon)
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
local TextButton = require(Plugin.App.Components.TextButton)
@ -128,6 +129,10 @@ function Notification:render()
)
end)
local logoAssetId = if self.props.thirdParty
then getThirdPartyIcon(self.props.source)
else Assets.Images.PluginButton
return e("TextButton", {
BackgroundTransparency = 1,
Size = size,
@ -149,7 +154,7 @@ function Notification:render()
}, {
Logo = e("ImageLabel", {
ImageTransparency = transparency,
Image = Assets.Images.PluginButton,
Image = logoAssetId,
BackgroundTransparency = 1,
Size = UDim2.new(0, logoSize, 0, logoSize),
Position = UDim2.new(0, 0, 0, 0),
@ -225,14 +230,15 @@ function Notifications:render()
for id, notif in self.props.notifications do
notifs["NotifID_" .. id] = e(Notification, {
soundPlayer = self.props.soundPlayer,
layoutOrder = (notif.timestamp - baseClock),
text = notif.text,
timestamp = notif.timestamp,
timeout = notif.timeout,
actions = notif.actions,
source = notif.source,
soundPlayer = self.props.soundPlayer,
thirdParty = notif.thirdParty,
layoutOrder = (notif.timestamp - baseClock),
name = notif.name,
source = notif.source,
onClose = function()
self.props.onClose(id)
end,

View file

@ -7,6 +7,8 @@ local Roact = require(Packages.Roact)
local Assets = require(Plugin.Assets)
local Theme = require(Plugin.App.Theme)
local getThirdPartyIcon = require(Plugin.getThirdPartyIcon)
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
local TextButton = require(Plugin.App.Components.TextButton)
@ -25,11 +27,7 @@ function PermissionPopup:render()
return Theme.with(function(theme)
local settingsTheme = theme.Settings
local thumbnail = Assets.Images.ThirdPartyPlugin
local thumbnailId = string.match(self.props.source, "cloud_(%d+)")
if thumbnailId then
thumbnail = string.format("rbxthumb://type=Asset&id=%s&w=150&h=150", thumbnailId)
end
local iconAsset = getThirdPartyIcon(self.props.source)
local apiRequests = {
Event = {},
@ -171,7 +169,7 @@ function PermissionPopup:render()
ThirdPartyIcon = e("ImageLabel", {
BackgroundTransparency = 1,
Size = UDim2.new(0, 32, 0, 32),
Image = thumbnail,
Image = iconAsset,
LayoutOrder = 1,
}),

View file

@ -11,19 +11,22 @@ local Theme = require(Plugin.App.Theme)
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
local SlicedImage = require(Plugin.App.Components.SlicedImage)
local Tooltip = require(Plugin.App.Components.Tooltip)
local e = Roact.createElement
local DIVIDER_FADE_SIZE = 0.1
local Listing = Roact.Component:extend("Listing")
local SourceListing = Roact.Component:extend("SourceListing")
function Listing:init()
function SourceListing:init()
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
end
function Listing:render()
function SourceListing:render()
local callerInfoFromSource = self.props.callerInfoFromSource
return Theme.with(function(theme)
return e("Frame", {
Size = self.contentSize:map(function(value)
@ -68,26 +71,76 @@ function Listing:render()
}),
}),
Text = e("Frame", {
Info = e("Frame", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, {
Name = e("TextLabel", {
Text = self.props.name,
FontFace = theme.Font.Bold,
TextSize = theme.TextSize.Medium,
TextColor3 = theme.Settings.Setting.NameColor,
TextXAlignment = Enum.TextXAlignment.Left,
TextTransparency = self.props.transparency,
Size = UDim2.new(1, 0, 0, 17),
LayoutOrder = 1,
PluginDetails = e("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, theme.TextSize.Medium + theme.TextSize.Body + 4),
}, {
Icon = e("ImageLabel", {
Image = self.props.icon,
BackgroundTransparency = 1,
SizeConstraint = Enum.SizeConstraint.RelativeYY,
AnchorPoint = Vector2.new(0, 0.5),
Position = UDim2.fromScale(0, 0.5),
Size = UDim2.fromScale(0.95, 0.95),
}),
Name = e("TextLabel", {
Text = callerInfoFromSource.Name,
FontFace = theme.Font.Bold,
TextSize = theme.TextSize.Medium,
TextColor3 = theme.Settings.Setting.NameColor,
TextXAlignment = Enum.TextXAlignment.Left,
TextTransparency = self.props.transparency,
AutomaticSize = Enum.AutomaticSize.X,
Size = UDim2.new(0, 0, 0, theme.TextSize.Medium),
Position = UDim2.new(0, theme.TextSize.Medium + theme.TextSize.Body + 8, 0, 0),
BackgroundTransparency = 1,
}),
Creator = e(
"TextLabel",
{
Text = callerInfoFromSource.Creator,
FontFace = theme.Font.Main,
TextSize = theme.TextSize.Body,
TextColor3 = theme.Settings.Setting.NameColor,
TextXAlignment = Enum.TextXAlignment.Left,
TextTransparency = self.props.transparency,
AutomaticSize = Enum.AutomaticSize.X,
Size = UDim2.fromOffset(0, theme.TextSize.Body),
Position = UDim2.new(
0,
theme.TextSize.Medium + theme.TextSize.Body + 8,
0,
theme.TextSize.Medium + 2
),
BackgroundTransparency = 1,
},
if callerInfoFromSource.HasVerifiedBadge
then e(
"ImageLabel",
{
Image = Assets.Images.Icons.Verified,
BackgroundTransparency = 1,
SizeConstraint = Enum.SizeConstraint.RelativeYY,
AnchorPoint = Vector2.new(0, 0.5),
Position = UDim2.new(1, 3, 0.5, 0),
Size = UDim2.fromScale(0.8, 0.8),
},
e(Tooltip.Trigger, {
text = "Creator has a verified badge",
})
)
else nil
),
}),
Description = e("TextLabel", {
Text = self.props.description,
Text = callerInfoFromSource.Description,
FontFace = theme.Font.Thin,
LineHeight = 1.2,
TextSize = theme.TextSize.Body,
@ -98,7 +151,7 @@ function Listing:render()
Size = self.containerSize:map(function(value)
local textBounds = getTextBoundsAsync(
self.props.description,
callerInfoFromSource.Description,
theme.Font.Thin,
theme.TextSize.Body,
value.X - 40,
@ -114,6 +167,7 @@ function Listing:render()
Layout = e("UIListLayout", {
VerticalAlignment = Enum.VerticalAlignment.Center,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
FillDirection = Enum.FillDirection.Vertical,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 6),
@ -148,4 +202,4 @@ function Listing:render()
end)
end
return Listing
return SourceListing

View file

@ -6,11 +6,12 @@ local Roact = require(Packages.Roact)
local Assets = require(Plugin.Assets)
local Theme = require(Plugin.App.Theme)
local getThirdPartyIcon = require(Plugin.getThirdPartyIcon)
local IconButton = require(Plugin.App.Components.IconButton)
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
local Tooltip = require(Plugin.App.Components.Tooltip)
local Listing = require(script.Listing)
local SourceListing = require(script.SourceListing)
local e = Roact.createElement
@ -89,23 +90,20 @@ function PermissionsPage:render()
continue
end
local meta = self.props.headlessAPI:_getMetaFromSource(source)
sources[source] = e(Listing, {
local callerInfoFromSource = self.props.headlessAPI:_getCallerInfoFromSource(source)
sources[source] = e(SourceListing, {
layoutOrder = string.byte(source),
transparency = self.props.transparency,
name = meta.Name,
description = string.format(
"%s plugin%s",
meta.Type,
if meta.Creator then " by " .. meta.Creator else ""
),
callerInfoFromSource = callerInfoFromSource,
icon = getThirdPartyIcon(source),
onClick = function()
self.props.onEdit(
self.props.headlessAPI._sourceToPlugin[source],
source,
meta,
callerInfoFromSource,
self.props.headlessAPI._permissions[source] or {}
)
end,

View file

@ -294,6 +294,7 @@ function App:addNotification(
end
function App:addThirdPartyNotification(
name: string,
source: string,
text: string,
timeout: number?,
@ -315,6 +316,7 @@ function App:addThirdPartyNotification(
timeout = timeout or 3,
actions = actions,
thirdParty = true,
name = name,
source = source,
}
@ -988,8 +990,9 @@ function App:render()
})
end,
onEdit = function(plugin, source, meta, apiMap)
local name = meta.Name .. if meta.Creator then " by " .. meta.Creator else ""
onEdit = function(plugin, source, callerInfo, apiMap)
local name = callerInfo.Name
.. if callerInfo.Creator then " by " .. callerInfo.Creator else ""
local apiList = {}
for api in apiMap do
table.insert(apiList, api)

View file

@ -10,13 +10,21 @@ local Config = require(Plugin.Config)
local Settings = require(Plugin.Settings)
local ApiContext = require(Plugin.ApiContext)
local cloudIdInfoCache = {}
local cloudIdProductInfoCache = {}
local apiPermissionAllowlist = {
Version = true,
ProtocolVersion = true,
RequestAccess = true,
}
export type CallerInfo = {
Type: "Local" | "Cloud" | "Studio",
Name: string,
Description: string,
Creator: string,
HasVerifiedBadge: boolean?,
}
local API = {}
function API.new(app)
@ -99,14 +107,14 @@ function API.new(app)
local cloudId, cloudInstance = string.match(topLevel, "cloud_(%d-)%.(.-)[^%w_%-]")
if cloudId then
local info = cloudIdInfoCache[cloudId]
local info = cloudIdProductInfoCache[cloudId]
if info then
return info.Name .. " by " .. info.Creator.Name
else
local success, newInfo =
pcall(MarketplaceService.GetProductInfo, MarketplaceService, tonumber(cloudId), Enum.InfoType.Asset)
if success then
cloudIdInfoCache[cloudId] = newInfo
cloudIdProductInfoCache[cloudId] = newInfo
return newInfo.Name .. " by " .. newInfo.Creator.Name
end
end
@ -119,41 +127,110 @@ function API.new(app)
return "Command Bar"
end
function Rojo:_getMetaFromSource(source)
local localPlugin = string.match(source, "user_(.+)")
function Rojo:_getCallerInfo(): CallerInfo
local traceback = string.split(debug.traceback(), "\n")
local topLevel = traceback[#traceback - 1]
local localPlugin = string.match(topLevel, "user_(.-)%.")
if localPlugin then
return {
Type = "Local",
Name = localPlugin,
Description = "Locally installed plugin.",
Creator = "Unknown",
HasVerifiedBadge = false,
}
end
local cloudId = string.match(source, "cloud_(%d+)")
local cloudId, cloudInstance = string.match(topLevel, "cloud_(%d-)%.(.-)[^%w_%-]")
if cloudId then
local info = cloudIdInfoCache[cloudId]
local info = cloudIdProductInfoCache[cloudId]
if not info then
local success, newInfo =
pcall(MarketplaceService.GetProductInfo, MarketplaceService, tonumber(cloudId), Enum.InfoType.Asset)
if success then
cloudIdProductInfoCache[cloudId] = newInfo
info = newInfo
end
end
if info then
return {
Type = "Cloud",
Name = info.Name,
Description = info.Description,
Creator = info.Creator.Name,
HasVerifiedBadge = info.Creator.HasVerifiedBadge,
}
else
local success, newInfo =
pcall(MarketplaceService.GetProductInfo, MarketplaceService, tonumber(cloudId), Enum.InfoType.Asset)
if success then
cloudIdInfoCache[cloudId] = newInfo
return {
Type = "Cloud",
Name = newInfo.Name,
Creator = newInfo.Creator.Name,
}
end
return {
Type = "Cloud",
Name = cloudInstance,
Description = "Could not retrieve plugin asset info.",
Creator = "Unknown",
HasVerifiedBadge = false,
}
end
end
return {
Type = "Studio",
Name = "Command Bar",
Description = "Command bar in Roblox Studio.",
Creator = "Unknown",
HasVerifiedBadge = false,
}
end
function Rojo:_getCallerInfoFromSource(source: string): CallerInfo
local localPlugin = string.match(source, "user_(.+)")
if localPlugin then
return {
Type = "Local",
Name = localPlugin,
Description = "Locally installed plugin.",
Creator = "Unknown",
HasVerifiedBadge = false,
}
end
local cloudId = string.match(source, "cloud_(%d+)")
if cloudId then
local info = cloudIdProductInfoCache[cloudId]
if not info then
local success, newInfo =
pcall(MarketplaceService.GetProductInfo, MarketplaceService, tonumber(cloudId), Enum.InfoType.Asset)
if success then
cloudIdProductInfoCache[cloudId] = newInfo
info = newInfo
end
end
if info then
return {
Type = "Cloud",
Name = info.Name,
Description = info.Description,
Creator = info.Creator.Name,
HasVerifiedBadge = info.Creator.HasVerifiedBadge,
}
else
return {
Type = "Cloud",
Name = source,
Description = "Could not retrieve plugin asset info.",
Creator = "Unknown",
HasVerifiedBadge = false,
}
end
end
return {
Type = "Studio",
Name = "Command Bar",
Description = "Command bar in Roblox Studio.",
Creator = "Unknown",
HasVerifiedBadge = false,
}
end
@ -418,7 +495,13 @@ function API.new(app)
end
end
return app:addThirdPartyNotification(Rojo:_getCallerName(), msg, timeout, sanitizedActions)
return app:addThirdPartyNotification(
Rojo:_getCallerName(),
Rojo:_getCallerSource(),
msg,
timeout,
sanitizedActions
)
end
Rojo._apiDescriptions.GetHostAndPort = {

View file

@ -0,0 +1,17 @@
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Assets = require(Plugin.Assets)
return function(source: string?)
if not source then
return Assets.Images.ThirdPartyPlugin
end
local sourceId = string.match(source, "cloud_(%d+)")
if not sourceId then
return Assets.Images.ThirdPartyPlugin
end
return string.format("rbxthumb://type=Asset&id=%s&w=150&h=150", sourceId)
end