Add permission management listings

This commit is contained in:
boatbomber 2023-01-04 18:20:57 -05:00
parent 4275783ba4
commit 7c1afd5aa9
5 changed files with 256 additions and 0 deletions

View file

@ -0,0 +1,164 @@
local TextService = game:GetService("TextService")
local Rojo = script:FindFirstAncestor("Rojo")
local Plugin = Rojo.Plugin
local Packages = Rojo.Packages
local Roact = require(Packages.Roact)
local Settings = require(Plugin.Settings)
local Assets = require(Plugin.Assets)
local Theme = require(Plugin.App.Theme)
local Checkbox = require(Plugin.App.Components.Checkbox)
local Dropdown = require(Plugin.App.Components.Dropdown)
local IconButton = require(Plugin.App.Components.IconButton)
local SlicedImage = require(Plugin.App.Components.SlicedImage)
local e = Roact.createElement
local DIVIDER_FADE_SIZE = 0.1
local function getTextBounds(text, textSize, font, lineHeight, bounds)
local textBounds = TextService:GetTextSize(text, textSize, font, bounds)
local lineCount = textBounds.Y / textSize
local lineHeightAbsolute = textSize * lineHeight
return Vector2.new(textBounds.X, lineHeightAbsolute * lineCount - (lineHeightAbsolute - textSize))
end
local Listing = Roact.Component:extend("Listing")
function Listing: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()
return Theme.with(function(theme)
theme = theme.Settings
return e("Frame", {
Size = self.contentSize:map(function(value)
return UDim2.new(1, 0, 0, 20 + value.Y + 20)
end),
LayoutOrder = self.props.layoutOrder,
ZIndex = -self.props.layoutOrder,
BackgroundTransparency = 1,
[Roact.Change.AbsoluteSize] = function(object)
self.setContainerSize(object.AbsoluteSize)
end,
}, {
Settings = e("TextButton", {
Text = "",
BackgroundTransparency = 1,
Size = UDim2.fromOffset(28, 28),
Position = UDim2.fromScale(1, 0.5),
AnchorPoint = Vector2.new(1, 0.5),
[Roact.Event.Activated] = function()
self.props.onClick()
end,
}, {
Button = Theme.with(function(theme)
theme = theme.Checkbox
return e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = theme.Inactive.BorderColor,
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),
}, {
Icon = e("ImageLabel", {
Image = Assets.Images.Icons.Settings,
ImageColor3 = theme.Inactive.IconColor,
ImageTransparency = self.props.transparency,
Size = UDim2.new(0, 16, 0, 16),
Position = UDim2.new(0.5, 0, 0.5, 0),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
}),
})
end),
}),
Text = e("Frame", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, {
Name = e("TextLabel", {
Text = self.props.name,
Font = Enum.Font.GothamBold,
TextSize = 17,
TextColor3 = theme.Setting.NameColor,
TextXAlignment = Enum.TextXAlignment.Left,
TextTransparency = self.props.transparency,
Size = UDim2.new(1, 0, 0, 17),
LayoutOrder = 1,
BackgroundTransparency = 1,
}),
Description = e("TextLabel", {
Text = self.props.description,
Font = Enum.Font.Gotham,
LineHeight = 1.2,
TextSize = 14,
TextColor3 = theme.Setting.DescriptionColor,
TextXAlignment = Enum.TextXAlignment.Left,
TextTransparency = self.props.transparency,
TextWrapped = true,
Size = self.containerSize:map(function(value)
local textBounds = getTextBounds(
self.props.description, 14, Enum.Font.Gotham, 1.2,
Vector2.new(value.X - 40, math.huge)
)
return UDim2.new(1, - 40, 0, textBounds.Y)
end),
LayoutOrder = 2,
BackgroundTransparency = 1,
}),
Layout = e("UIListLayout", {
VerticalAlignment = Enum.VerticalAlignment.Center,
FillDirection = Enum.FillDirection.Vertical,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 6),
[Roact.Change.AbsoluteContentSize] = function(object)
self.setContentSize(object.AbsoluteContentSize)
end,
}),
Padding = e("UIPadding", {
PaddingTop = UDim.new(0, 20),
PaddingBottom = UDim.new(0, 20),
}),
}),
Divider = e("Frame", {
BackgroundColor3 = theme.DividerColor,
BackgroundTransparency = self.props.transparency,
Size = UDim2.new(1, 0, 0, 1),
BorderSizePixel = 0,
}, {
Gradient = e("UIGradient", {
Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(DIVIDER_FADE_SIZE, 0),
NumberSequenceKeypoint.new(1 - DIVIDER_FADE_SIZE, 0),
NumberSequenceKeypoint.new(1, 1),
}),
}),
}),
})
end)
end
return Listing

View file

@ -11,6 +11,7 @@ local Theme = require(Plugin.App.Theme)
local IconButton = require(Plugin.App.Components.IconButton) local IconButton = require(Plugin.App.Components.IconButton)
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame) local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
local Tooltip = require(Plugin.App.Components.Tooltip) local Tooltip = require(Plugin.App.Components.Tooltip)
local Listing = require(script.Listing)
local e = Roact.createElement local e = Roact.createElement
@ -64,6 +65,42 @@ function PermissionsPage:render()
return Theme.with(function(theme) return Theme.with(function(theme)
theme = theme.Settings theme = theme.Settings
local sources = {}
if next(self.props.headlessAPI._permissions) == nil then
sources.noSources = e("TextLabel", {
Text = "No third-party plugins have been granted permissions.",
Font = Enum.Font.Gotham,
TextSize = 18,
TextColor3 = theme.Setting.DescriptionColor,
TextTransparency = self.props.transparency,
TextWrapped = true,
Size = UDim2.new(1, 0, 0, 48),
LayoutOrder = 0,
BackgroundTransparency = 1,
})
else
for source in self.props.headlessAPI._permissions do
local meta = self.props.headlessAPI:_getMetaFromSource(source)
sources[source] = e(Listing, {
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 ""
),
onClick = function()
self.props.onEdit(source, meta, self.props.headlessAPI._permissions[source] or {})
end,
})
end
end
return e(ScrollingFrame, { return e(ScrollingFrame, {
size = UDim2.new(1, 0, 1, 0), size = UDim2.new(1, 0, 1, 0),
contentSize = self.contentSize, contentSize = self.contentSize,
@ -84,6 +121,8 @@ function PermissionsPage:render()
end, end,
}), }),
Sources = Roact.createFragment(sources),
Padding = e("UIPadding", { Padding = e("UIPadding", {
PaddingLeft = UDim.new(0, 20), PaddingLeft = UDim.new(0, 20),
PaddingRight = UDim.new(0, 20), PaddingRight = UDim.new(0, 20),

View file

@ -519,11 +519,26 @@ function App:render()
}), }),
Permissions = createPageElement(AppStatus.Permissions, { Permissions = createPageElement(AppStatus.Permissions, {
headlessAPI = self.headlessAPI,
onBack = function() onBack = function()
self:setState({ self:setState({
appStatus = AppStatus.Settings, appStatus = AppStatus.Settings,
}) })
end, end,
onEdit = function(source, meta, apiMap)
local apiList = {}
for api in apiMap do
table.insert(apiList, api)
end
self:requestPermission(
source,
meta.Name .. if meta.Creator then " by " .. meta.Creator else "",
apiList,
apiMap
)
end,
}), }),
Error = createPageElement(AppStatus.Error, { Error = createPageElement(AppStatus.Error, {

View file

@ -26,6 +26,7 @@ local Assets = {
Back = "rbxassetid://6017213752", Back = "rbxassetid://6017213752",
Reset = "rbxassetid://10142422327", Reset = "rbxassetid://10142422327",
Expand = "rbxassetid://12045401097", Expand = "rbxassetid://12045401097",
Settings = "rbxassetid://12046309515",
}, },
Checkbox = { Checkbox = {
Active = "rbxassetid://6016251644", Active = "rbxassetid://6016251644",

View file

@ -95,6 +95,43 @@ function API.new(app)
return "Command Bar" return "Command Bar"
end end
function Rojo:_getMetaFromSource(source)
local localPlugin = string.match(source, "user_(.+)")
if localPlugin then
return {
Type = "Local",
Name = localPlugin,
}
end
local cloudId = string.match(source, "cloud_(%d+)")
if cloudId then
local info = cloudIdInfoCache[cloudId]
if info then
return {
Type = "Cloud",
Name = info.Name,
Creator = info.Creator.Name,
}
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
end
end
return {
Type = "Studio",
Name = "Command Bar",
}
end
function Rojo:_getCallerType() function Rojo:_getCallerType()
local traceback = string.split(debug.traceback(), "\n") local traceback = string.split(debug.traceback(), "\n")
local topLevel = traceback[#traceback - 1] local topLevel = traceback[#traceback - 1]