forked from rojo-rbx/rojo
Show update indicator on version header (#1069)
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# Rojo Changelog
|
# Rojo Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
* Added an update indicator to the version header when a new version of the plugin is available. ([#1069])
|
||||||
|
|
||||||
|
[#1069]: https://github.com/rojo-rbx/rojo/pull/1069
|
||||||
|
|
||||||
## 7.5.1 - April 25th, 2025
|
## 7.5.1 - April 25th, 2025
|
||||||
* Fixed output spam related to `Instance.Capabilities` in the plugin
|
* Fixed output spam related to `Instance.Capabilities` in the plugin
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,70 @@ local Assets = require(Plugin.Assets)
|
|||||||
local Config = require(Plugin.Config)
|
local Config = require(Plugin.Config)
|
||||||
local Version = require(Plugin.Version)
|
local Version = require(Plugin.Version)
|
||||||
|
|
||||||
|
local Tooltip = require(Plugin.App.Components.Tooltip)
|
||||||
|
local SlicedImage = require(script.Parent.SlicedImage)
|
||||||
|
|
||||||
local e = Roact.createElement
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local function VersionIndicator(props)
|
||||||
|
local updateMessage = Version.getUpdateMessage()
|
||||||
|
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("Frame", {
|
||||||
|
LayoutOrder = props.layoutOrder,
|
||||||
|
Size = UDim2.new(0, 0, 0, 25),
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
AutomaticSize = Enum.AutomaticSize.X,
|
||||||
|
}, {
|
||||||
|
Border = if updateMessage
|
||||||
|
then e(SlicedImage, {
|
||||||
|
slice = Assets.Slices.RoundedBorder,
|
||||||
|
color = theme.Button.Bordered.Enabled.BorderColor,
|
||||||
|
transparency = props.transparency,
|
||||||
|
size = UDim2.fromScale(1, 1),
|
||||||
|
zIndex = 0,
|
||||||
|
}, {
|
||||||
|
Indicator = e("ImageLabel", {
|
||||||
|
Size = UDim2.new(0, 10, 0, 10),
|
||||||
|
ScaleType = Enum.ScaleType.Fit,
|
||||||
|
Image = Assets.Images.Circles[16],
|
||||||
|
ImageColor3 = theme.Header.LogoColor,
|
||||||
|
ImageTransparency = props.transparency,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Position = UDim2.new(1, 0, 0, 0),
|
||||||
|
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
|
|
||||||
|
Tip = if updateMessage
|
||||||
|
then e(Tooltip.Trigger, {
|
||||||
|
text = updateMessage,
|
||||||
|
delay = 0.1,
|
||||||
|
})
|
||||||
|
else nil,
|
||||||
|
|
||||||
|
VersionText = e("TextLabel", {
|
||||||
|
Text = Version.display(Config.version),
|
||||||
|
FontFace = theme.Font.Thin,
|
||||||
|
TextSize = theme.TextSize.Body,
|
||||||
|
TextColor3 = theme.Header.VersionColor,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextTransparency = props.transparency,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, 0, 1, 0),
|
||||||
|
AutomaticSize = Enum.AutomaticSize.X,
|
||||||
|
}, {
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 6),
|
||||||
|
PaddingRight = UDim.new(0, 6),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local function Header(props)
|
local function Header(props)
|
||||||
return Theme.with(function(theme)
|
return Theme.with(function(theme)
|
||||||
return e("Frame", {
|
return e("Frame", {
|
||||||
@@ -29,18 +91,9 @@ local function Header(props)
|
|||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Version = e("TextLabel", {
|
VersionIndicator = e(VersionIndicator, {
|
||||||
Text = Version.display(Config.version),
|
transparency = props.transparency,
|
||||||
FontFace = theme.Font.Thin,
|
layoutOrder = 2,
|
||||||
TextSize = theme.TextSize.Body,
|
|
||||||
TextColor3 = theme.Header.VersionColor,
|
|
||||||
TextXAlignment = Enum.TextXAlignment.Left,
|
|
||||||
TextTransparency = props.transparency,
|
|
||||||
|
|
||||||
Size = UDim2.new(1, 0, 0, theme.TextSize.Body),
|
|
||||||
|
|
||||||
LayoutOrder = 2,
|
|
||||||
BackgroundTransparency = 1,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
Layout = e("UIListLayout", {
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ function Trigger:managePopup()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.showDelayThread = task.delay(DELAY, function()
|
self.showDelayThread = task.delay(self.props.delay or DELAY, function()
|
||||||
self.props.context.addTip(self.id, {
|
self.props.context.addTip(self.id, {
|
||||||
Text = self.props.text,
|
Text = self.props.text,
|
||||||
Position = self:getMousePos(),
|
Position = self:getMousePos(),
|
||||||
|
|||||||
@@ -251,27 +251,10 @@ function App:closeNotification(id: number)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function App:checkForUpdates()
|
function App:checkForUpdates()
|
||||||
if not Settings:get("checkForUpdates") then
|
local updateMessage = Version.getUpdateMessage()
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local isLocalInstall = string.find(debug.traceback(), "\n[^\n]-user_.-$") ~= nil
|
if updateMessage then
|
||||||
local latestCompatibleVersion = Version.retrieveLatestCompatible({
|
self:addNotification(updateMessage, 500, {
|
||||||
version = Config.version,
|
|
||||||
includePrereleases = isLocalInstall and Settings:get("checkForPrereleases"),
|
|
||||||
})
|
|
||||||
if not latestCompatibleVersion then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self:addNotification(
|
|
||||||
string.format(
|
|
||||||
"A newer compatible version of Rojo, %s, was published %s! Go to the Rojo releases page to learn more.",
|
|
||||||
Version.display(latestCompatibleVersion.version),
|
|
||||||
timeUtil.elapsedToText(DateTime.now().UnixTimestamp - latestCompatibleVersion.publishedUnixTimestamp)
|
|
||||||
),
|
|
||||||
500,
|
|
||||||
{
|
|
||||||
Dismiss = {
|
Dismiss = {
|
||||||
text = "Dismiss",
|
text = "Dismiss",
|
||||||
style = "Bordered",
|
style = "Bordered",
|
||||||
@@ -280,8 +263,8 @@ function App:checkForUpdates()
|
|||||||
notification:dismiss()
|
notification:dismiss()
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
)
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function App:getPriorSyncInfo(): { host: string?, port: string?, projectName: string?, timestamp: number? }
|
function App:getPriorSyncInfo(): { host: string?, port: string?, projectName: string?, timestamp: number? }
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
local Packages = script.Parent.Parent.Packages
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
local Packages = Rojo.Packages
|
||||||
|
|
||||||
local Http = require(Packages.Http)
|
local Http = require(Packages.Http)
|
||||||
local Promise = require(Packages.Promise)
|
local Promise = require(Packages.Promise)
|
||||||
|
local Log = require(Packages.Log)
|
||||||
|
|
||||||
|
local Config = require(Plugin.Config)
|
||||||
|
local Settings = require(Plugin.Settings)
|
||||||
|
local timeUtil = require(Plugin.timeUtil)
|
||||||
|
|
||||||
|
type LatestReleaseInfo = {
|
||||||
|
version: { number },
|
||||||
|
prerelease: boolean,
|
||||||
|
publishedUnixTimestamp: number,
|
||||||
|
}
|
||||||
|
|
||||||
local function compare(a, b)
|
local function compare(a, b)
|
||||||
if a > b then
|
if a > b then
|
||||||
@@ -88,14 +102,26 @@ function Version.display(version)
|
|||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
The GitHub API rate limit for unauthenticated requests is rather low,
|
||||||
|
and we don't release often enough to warrant checking it more than once a day.
|
||||||
|
--]]
|
||||||
|
Version._cachedLatestCompatible = nil :: {
|
||||||
|
value: LatestReleaseInfo?,
|
||||||
|
timestamp: number,
|
||||||
|
}?
|
||||||
|
|
||||||
function Version.retrieveLatestCompatible(options: {
|
function Version.retrieveLatestCompatible(options: {
|
||||||
version: { number },
|
version: { number },
|
||||||
includePrereleases: boolean?,
|
includePrereleases: boolean?,
|
||||||
}): {
|
}): LatestReleaseInfo?
|
||||||
version: { number },
|
if Version._cachedLatestCompatible and os.clock() - Version._cachedLatestCompatible.timestamp < 60 * 60 * 24 then
|
||||||
prerelease: boolean,
|
Log.debug("Using cached latest compatible version")
|
||||||
publishedUnixTimestamp: number,
|
return Version._cachedLatestCompatible.value
|
||||||
}?
|
end
|
||||||
|
|
||||||
|
Log.debug("Retrieving latest compatible version from GitHub")
|
||||||
|
|
||||||
local success, releases = Http.get("https://api.github.com/repos/rojo-rbx/rojo/releases?per_page=10")
|
local success, releases = Http.get("https://api.github.com/repos/rojo-rbx/rojo/releases?per_page=10")
|
||||||
:andThen(function(response)
|
:andThen(function(response)
|
||||||
if response.code >= 400 then
|
if response.code >= 400 then
|
||||||
@@ -114,7 +140,7 @@ function Version.retrieveLatestCompatible(options: {
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Iterate through releases, looking for the latest compatible version
|
-- Iterate through releases, looking for the latest compatible version
|
||||||
local latestCompatible = nil
|
local latestCompatible: LatestReleaseInfo? = nil
|
||||||
for _, release in releases do
|
for _, release in releases do
|
||||||
-- Skip prereleases if they are not requested
|
-- Skip prereleases if they are not requested
|
||||||
if (not options.includePrereleases) and release.prerelease then
|
if (not options.includePrereleases) and release.prerelease then
|
||||||
@@ -142,10 +168,43 @@ function Version.retrieveLatestCompatible(options: {
|
|||||||
|
|
||||||
-- Don't return anything if the latest found is not newer than the current version
|
-- Don't return anything if the latest found is not newer than the current version
|
||||||
if latestCompatible == nil or Version.compare(latestCompatible.version, options.version) <= 0 then
|
if latestCompatible == nil or Version.compare(latestCompatible.version, options.version) <= 0 then
|
||||||
|
-- Cache as nil so we don't try again for a day
|
||||||
|
Version._cachedLatestCompatible = {
|
||||||
|
value = nil,
|
||||||
|
timestamp = os.clock(),
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cache the latest compatible version
|
||||||
|
Version._cachedLatestCompatible = {
|
||||||
|
value = latestCompatible,
|
||||||
|
timestamp = os.clock(),
|
||||||
|
}
|
||||||
|
|
||||||
return latestCompatible
|
return latestCompatible
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Version.getUpdateMessage(): string?
|
||||||
|
if not Settings:get("checkForUpdates") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local isLocalInstall = string.find(debug.traceback(), "\n[^\n]-user_.-$") ~= nil
|
||||||
|
local latestCompatibleVersion = Version.retrieveLatestCompatible({
|
||||||
|
version = Config.version,
|
||||||
|
includePrereleases = isLocalInstall and Settings:get("checkForPrereleases"),
|
||||||
|
})
|
||||||
|
if not latestCompatibleVersion then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
return string.format(
|
||||||
|
"A newer compatible version of Rojo, %s, was published %s! Go to the Rojo releases page to learn more.",
|
||||||
|
Version.display(latestCompatibleVersion.version),
|
||||||
|
timeUtil.elapsedToText(DateTime.now().UnixTimestamp - latestCompatibleVersion.publishedUnixTimestamp)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
return Version
|
return Version
|
||||||
|
|||||||
Reference in New Issue
Block a user