Compare commits

...

42 Commits

Author SHA1 Message Date
Oğuzhan Koral d2198c0765 Improve refreshMixpanelIds function with better registration 2023-02-17 02:16:28 +03:00
oguzhankoral 549bf63198 Improve refreshMixpanelIds function with better registration and identifying 2023-02-16 16:26:00 +03:00
Oğuzhan Koral 8b824f5342 Update innosetup workflow
There were issues with innosetup workflow which does not run one powershell.exe,
so workflows splitted into chunks according to shells
2023-02-08 21:20:17 +03:00
oguzhankoral d052d5e8a1 Change set env variable to run with powershell 2023-02-08 21:12:00 +03:00
oguzhankoral 610c22dd02 Update innosetup workflow 2023-02-08 21:05:02 +03:00
Oğuzhan Koral a5496ab6a9 Feat (CI): Run CI step to create env variables for innosetup 2023-02-08 20:14:23 +03:00
oguzhankoral c29c8f009c Remove duplicated context: innosetup on yaml file 2023-02-02 10:34:34 +02:00
oguzhankoral fb7e9f2a6c Run CI step to create env variables for innosetup 2023-02-01 23:42:25 +02:00
Oğuzhan Koral 62c2bbb9fa Chore (Notification): Add error notification to GlobalToast 2023-01-23 15:34:03 +03:00
oguzhankoral 7656772194 Add error notification to GlobalToast 2023-01-23 15:31:13 +03:00
Oğuzhan Koral f22ff050e0 Fix (Stream, Attributes): Follow up fixes add stream by url and attributes 2023-01-23 12:35:22 +03:00
oguzhankoral 708f0b44fd Check face entity attributes settings before merging faces into mesh 2023-01-23 12:01:47 +03:00
oguzhankoral 2ee4581f17 Check stream role is null first before split 2023-01-23 12:00:13 +03:00
Oğuzhan Koral e337fb869f Feat (Settings) Advanced settings for entity specific attributes 2023-01-22 00:09:05 +03:00
oguzhankoral 4a8b0147e1 Create UI components for entity specific settings 2023-01-22 00:03:18 +03:00
oguzhankoral c95a1c7e1f Consider entity settings on speckle objects 2023-01-22 00:02:51 +03:00
oguzhankoral a07cd5c3f5 Extend model preferences with entity specific settings 2023-01-22 00:02:24 +03:00
Oğuzhan Koral 4d1473582e Feat (Stream): Add streams by URL or id 2023-01-21 17:33:35 +03:00
oguzhankoral 3bc9f4c452 Fix rubocop issue 2023-01-21 17:33:15 +03:00
oguzhankoral 11377038a0 Implement and make functional adding streams by url 2023-01-20 22:26:36 +03:00
Oğuzhan Koral 6053d3eac1 Fix (Attributes): Rescue from problematic dictionaries 2023-01-20 11:50:18 +03:00
oguzhankoral 529830f36b Rescue from problematic dictionaries 2023-01-20 11:47:34 +03:00
Oğuzhan Koral 505cf6265c Feat (Scene): Send and receive scenes 2023-01-19 20:09:19 +03:00
oguzhankoral 8cd9673eec Send scenes to speckle 2023-01-19 20:05:44 +03:00
oguzhankoral ac9cb28558 Receive named views as scene 2023-01-19 16:38:32 +03:00
Oğuzhan Koral 6eefe0698c Fix (Branch): Activate created branch 2023-01-19 12:17:58 +03:00
oguzhankoral d0113532b6 Activate created branch 2023-01-19 11:09:23 +03:00
Oğuzhan Koral 5a1d2ad5f4 Feat (Branch): Create branch button
Thanks Fabians for helps
2023-01-18 17:02:20 +03:00
oguzhankoral dfe02f4c74 Rename CreateBranchDialog 2023-01-18 16:58:05 +03:00
oguzhankoral 5349d556b9 Group dialogs to folder 2023-01-18 16:21:43 +03:00
oguzhankoral 7f7d8a501b Add notes about refresh 2023-01-18 15:58:43 +03:00
oguzhankoral cbee0e465b Remove unused combineFacesByMaterialHandler method 2023-01-18 15:44:20 +03:00
oguzhankoral faf00f0b0d Refetch stream on StreamCard whenever branch created 2023-01-18 15:42:33 +03:00
oguzhankoral fa97da5781 Track create stream and branch via mixpanel 2023-01-18 15:03:14 +03:00
oguzhankoral e7bab546db Remove loggings 2023-01-18 14:16:52 +03:00
oguzhankoral e2f4a30b5b Fix updating problem on created branch
- Thanks to Fabians
2023-01-18 11:53:50 +03:00
oguzhankoral ccff1df041 Add tooltip to create branch method 2023-01-18 11:53:11 +03:00
oguzhankoral 2037cfc25a Import and use CreateBranchDialog per stream card 2023-01-17 19:24:53 +03:00
oguzhankoral 268a091d8a Add dialog for branch creation 2023-01-17 19:23:44 +03:00
Oğuzhan Koral 6b52dfab3e Chore (Theming): Store light mode setting separately 2023-01-17 16:16:39 +03:00
oguzhankoral 839999851f Correct preference hash for theming 2023-01-17 16:12:21 +03:00
oguzhankoral a87470b7b5 Split Sketchup settings 2023-01-17 16:11:56 +03:00
25 changed files with 693 additions and 76 deletions
+33 -11
View File
@@ -36,19 +36,41 @@ jobs:
- attach_workspace:
at: ./
- run:
name: Patch
name: Create Innosetup signing cert
shell: powershell.exe
command:
| # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
$semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
command: |
echo $env:PFX_B64 > "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt"
certutil -decode "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt" "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.pfx"
- run:
name: Set Environment Variable
shell: powershell.exe
command: |
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "2.0.999" } else { $env:CIRCLE_TAG }
$semver = if($tag.Contains('/')) {$tag.Split("/")[0] } else { $tag }
$ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
$channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
$version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
echo $version
$version = "$($ver).$($env:WORKFLOW_NUM)"
python patch_version.py $semver
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
environment:
WORKFLOW_NUM: << pipeline.number >>
- run:
name: Build Installer
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss /Sbyparam=$p
shell: cmd.exe #does not work in powershell
#- run:
# name: Patch
# shell: powershell.exe
# command:
# | # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
# $tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
# $semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
# $ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
# $channel = if($semver.Contains('-')) {$semver.Split("-")[1] } else { "latest" }
# $version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
# New-Item -Force "speckle-sharp-ci-tools/Installers/sketchup/$channel.yml" -ItemType File -Value "version: $semver"
# echo $version
# python patch_version.py $semver
# speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\sketchup.iss
- persist_to_workspace:
root: ./
paths:
@@ -115,7 +137,6 @@ workflows:
only: /.*/
- build-connector:
context: innosetup
slug: sketchup
requires:
- get-ci-tools
@@ -123,6 +144,7 @@ workflows:
filters:
tags:
only: /.*/
context: innosetup
- deploy-manager2:
context: do-spaces-speckle-releases
@@ -0,0 +1,20 @@
# frozen_string_literal: true
require_relative 'action'
module SpeckleConnector
module Actions
# Action to collect versions from sketchup and connector to track user's version by mixpanel.
class CollectVersions < Action
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
versions = {
sketchup: Sketchup.version.to_i,
speckle: SpeckleConnector::CONNECTOR_VERSION
}
state.with_add_queue('collectVersions', versions.to_json, [])
end
end
end
end
@@ -9,7 +9,8 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def self.update_state(state, _data)
(saved_streams = state.sketchup_state.sketchup_model.attribute_dictionary('speckle', true)['streams']) or []
(saved_streams = state.sketchup_state.sketchup_model
.attribute_dictionary('Speckle', true)['saved_streams']) or []
state.with_add_queue('setSavedStreams', saved_streams, [])
end
end
@@ -18,10 +18,10 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('speckle', true)
saved = speckle_dict['streams'] || []
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('Speckle', true)
saved = speckle_dict['saved_streams'] || []
saved -= [@stream_id]
speckle_dict['streams'] = saved
speckle_dict['saved_streams'] = saved
state
end
end
+3 -3
View File
@@ -16,10 +16,10 @@ module SpeckleConnector
# @param state [States::State] the current state of the {App::SpeckleConnectorApp}
# @return [States::State] the new updated state object
def update_state(state)
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('speckle', true)
saved = speckle_dict['streams'] || []
speckle_dict = state.sketchup_state.sketchup_model.attribute_dictionary('Speckle', true)
saved = speckle_dict['saved_streams'] || []
saved = saved.empty? ? [@stream_id] : saved.unshift(@stream_id)
speckle_dict['streams'] = saved
speckle_dict['saved_streams'] = saved
state
end
end
@@ -16,11 +16,10 @@ module SpeckleConnector
# @return [States::State] the new updated state object
def update_state(state)
to_send_stream_id = state.speckle_state.stream_queue[:stream_id]
return state if to_send_stream_id == @stream_id
return state if to_send_stream_id == @stream_id || to_send_stream_id.nil?
to_send_converted = state.speckle_state.stream_queue[:converted].to_json
new_state = state.with_add_queue('convertedFromSketchup', to_send_stream_id, [to_send_converted])
new_state = new_state.with_add_queue('oneClickSend', to_send_stream_id, [])
new_state.with_empty_stream_queue
end
end
+20 -1
View File
@@ -48,7 +48,9 @@ module SpeckleConnector
# @param obj [Object] speckle commit object.
def receive_commit_object(obj, model_preferences)
# First create layers on the sketchup before starting traversing
create_layers(obj.keys.filter_map { |key| key if key.start_with?('@') }, sketchup_model.layers)
filtered_layer_containers = obj.keys.filter_map { |key| key if key.start_with?('@') && key != '@Named Views' }
create_layers(filtered_layer_containers, sketchup_model.layers)
create_views(obj.filter_map { |key, value| value if key == '@Named Views' }, sketchup_model)
# Define default commit layer which is the fallback
default_commit_layer = sketchup_model.layers.layers.find { |layer| layer.display_name == '@Untagged' }
traverse_commit_object(obj, sketchup_model.layers, default_commit_layer, model_preferences)
@@ -70,6 +72,23 @@ module SpeckleConnector
create_folder_layers(folder_layer_arrays, folder)
end
# @param views [Array] views.
# @param sketchup_model [Sketchup::Model] active sketchup model.
def create_views(views, sketchup_model)
return if views.empty?
views.first.each do |view|
origin = view['origin']
target = view['target']
origin = SpeckleObjects::Geometry::Point.to_native(origin['x'], origin['y'], origin['z'], origin['units'])
target = SpeckleObjects::Geometry::Point.to_native(target['x'], target['y'], target['z'], target['units'])
# Set camera position before creating scene on it.
my_camera = Sketchup::Camera.new(origin, target, [0, 0, 1], !view['isOrthogonal'], view['lens'])
sketchup_model.active_view.camera = my_camera
sketchup_model.pages.add(view['name'])
end
end
# @param headless_layers [Array<String>] headless layer names.
# @param folder [Sketchup::Layers, Sketchup::LayerFolder] layer folder to create commit layers under it.
def create_headless_layers(headless_layers, folder)
@@ -4,9 +4,11 @@ require_relative 'converter'
require_relative 'base_object_serializer'
require_relative '../speckle_objects/base'
require_relative '../speckle_objects/geometry/line'
require_relative '../speckle_objects/geometry/length'
require_relative '../speckle_objects/geometry/mesh'
require_relative '../speckle_objects/other/block_instance'
require_relative '../speckle_objects/other/block_definition'
require_relative '../speckle_objects/built_elements/view3d'
module SpeckleConnector
module Converters
@@ -30,9 +32,48 @@ module SpeckleConnector
end
# send only layers that have any object
base_object_properties = layers.reject { |_layer_name, objects| objects.empty? }
add_views(base_object_properties) if sketchup_model.pages.any?
SpeckleObjects::Base.with_detached_layers(base_object_properties)
end
# Add views from pages.
# @param base_object_properties [Hash] dynamically attached base object properties.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def add_views(base_object_properties)
views = []
sketchup_model.pages.each do |page|
cam = page.camera
origin = SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(cam.eye[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.eye[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.eye[2], @units),
@units
)
target = SpeckleObjects::Geometry::Point.new(
SpeckleObjects::Geometry.length_to_speckle(cam.target[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.target[2], @units),
@units
)
direction = SpeckleObjects::Geometry::Vector.new(
SpeckleObjects::Geometry.length_to_speckle(cam.direction[0], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[1], @units),
SpeckleObjects::Geometry.length_to_speckle(cam.direction[2], @units),
@units
)
view = SpeckleObjects::BuiltElements::View3d.new(
page.name,
origin, target, direction, SpeckleObjects::Geometry::Vector.new(0, 0, 1, @units),
cam.perspective?, cam.fov, @units, page.name
)
views.append(view)
end
base_object_properties['@Named Views'] = views
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
# Serialized and traversed information to send batches.
# @param base [SpeckleObjects::Base] base object to serialize.
# @return [String, Integer, Array<Object>] base id, total_children_count of base and batches
@@ -10,6 +10,7 @@ module SpeckleConnector
module Preferences
include Immutable::ImmutableUtils
DICT_HANDLER = SketchupModel::Dictionary::SpeckleModelDictionaryHandler
DEFAULT_PREFERENCES = "('configSketchup', '{\"DarkTheme\":false}');"
# @param sketchup_model [Sketchup::Model] active model.
# rubocop:disable Metrics/MethodLength
@@ -17,8 +18,13 @@ module SpeckleConnector
# Init sqlite database
db = Sqlite3::Database.new(SPECKLE_CONFIG_DB_PATH)
# Check configSketchup key is valid or not, otherwise init with default settings
if db.exec("SELECT content FROM 'objects' WHERE hash = 'configSketchup'").empty?
db.exec("INSERT INTO 'objects' VALUES #{DEFAULT_PREFERENCES}")
end
# Select data
data = db.exec("SELECT content FROM 'objects' WHERE hash = 'configDUI'").first.first
data = db.exec("SELECT content FROM 'objects' WHERE hash = 'configSketchup'").first.first
# Parse string to hash
data_hash = JSON.parse(data).to_h
@@ -35,12 +41,41 @@ module SpeckleConnector
dark_theme: dark_theme
},
model: {
combine_faces_by_material: DICT_HANDLER.get_attribute(sketchup_model,
:combine_faces_by_material, 'Speckle'),
include_entity_attributes: DICT_HANDLER.get_attribute(sketchup_model,
:include_entity_attributes, 'Speckle'),
merge_coplanar_faces: DICT_HANDLER.get_attribute(sketchup_model,
:merge_coplanar_faces, 'Speckle')
combine_faces_by_material: DICT_HANDLER.get_attribute(
sketchup_model,
:combine_faces_by_material,
'Speckle'
),
include_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_entity_attributes,
'Speckle'
),
include_face_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_face_entity_attributes,
'Speckle'
),
include_edge_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_edge_entity_attributes,
'Speckle'
),
include_group_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_group_entity_attributes,
'Speckle'
),
include_component_entity_attributes: DICT_HANDLER.get_attribute(
sketchup_model,
:include_component_entity_attributes,
'Speckle'
),
merge_coplanar_faces: DICT_HANDLER.get_attribute(
sketchup_model,
:merge_coplanar_faces,
'Speckle'
)
}
}
)
@@ -62,6 +97,10 @@ module SpeckleConnector
{
combine_faces_by_material: true,
include_entity_attributes: true,
include_face_entity_attributes: true,
include_edge_entity_attributes: true,
include_group_entity_attributes: true,
include_component_entity_attributes: true,
merge_coplanar_faces: true
}
end
@@ -28,7 +28,10 @@ module SpeckleConnector
dictionaries.each do |dict_name, entries|
dict_name = dict_name == 'empty_dictionary_name' ? '' : dict_name
JSON.parse(entries).each do |key, value|
entity.set_attribute(dict_name, key, value)
set_attribute(entity, key, value, dict_name)
rescue StandardError => e
puts("Failed to write key: #{key} value: #{value} to dictionary #{dict_name}")
puts(e)
end
end
end
@@ -0,0 +1,45 @@
# frozen_string_literal: true
require_relative '../base'
require_relative '../../speckle_objects/geometry/point'
require_relative '../../speckle_objects/geometry/vector'
module SpeckleConnector
module SpeckleObjects
module BuiltElements
# View3d object represents scenes on Sketchup.
class View3d < Base
SPECKLE_TYPE = 'Objects.BuiltElements.View:Objects.BuiltElements.View3D'
# @param name [String] name of the scene
# @param origin [SpeckleObjects::Geometry::Point] origin (eye) of the view.
# @param target [SpeckleObjects::Geometry::Point] target of the view.
# @param direction [SpeckleObjects::Geometry::Vector] direction of the view from eye to target.
# @param up_direction [SpeckleObjects::Geometry::Vector] up direction of the view.
# @param is_perspective [Boolean] whether view is perspective or not.
# @param lens [Boolean] fov value of the view camera.
# @param units [String] units of the camera.
# @param application_id [String] application_id of the view.
# rubocop:disable Metrics/ParameterLists
def initialize(name, origin, target, direction, up_direction,
is_perspective, lens, units, application_id)
super(
speckle_type: SPECKLE_TYPE,
total_children_count: 0,
application_id: application_id,
id: nil
)
self[:name] = name
self[:origin] = origin
self[:target] = target
self[:forwardDirection] = direction
self[:upDirection] = up_direction
self[:isOrthogonal] = !is_perspective
self[:lens] = lens
self[:units] = units
end
# rubocop:enable Metrics/ParameterLists
end
end
end
end
@@ -40,7 +40,7 @@ module SpeckleConnector
# @param edge [Sketchup::Edge] edge to convert line.
def self.from_edge(edge, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes]
if model_preferences[:include_entity_attributes] && model_preferences[:include_edge_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(edge)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
@@ -89,9 +89,10 @@ module SpeckleConnector
# @param face [Sketchup::Face] face to convert mesh
# rubocop:disable Style/MultilineTernaryOperator
# rubocop:disable Metrics/CyclomaticComplexity
def self.from_face(face, units, model_preferences)
dictionaries = {}
if model_preferences[:include_entity_attributes]
if model_preferences[:include_entity_attributes] && model_preferences[:include_face_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(face)
end
has_any_soften_edge = face.edges.any?(&:soft?)
@@ -112,6 +113,7 @@ module SpeckleConnector
speckle_mesh
end
# rubocop:enable Style/MultilineTernaryOperator
# rubocop:enable Metrics/CyclomaticComplexity
def face_to_mesh(face)
mesh = face.loops.count > 1 ? face.mesh : nil
@@ -47,13 +47,21 @@ module SpeckleConnector
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def self.from_definition(definition, units, definitions, preferences, &convert)
guid = definition.guid
return definitions[guid] if definitions.key?(guid)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(definition)
if definition.group?
if preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(definition)
end
elsif preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(definition)
end
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
@@ -80,6 +88,7 @@ module SpeckleConnector
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
# Finds or creates a component definition from the geometry and the given name
# @param sketchup_model [Sketchup::Model] sketchup model to check block definitions.
@@ -164,7 +173,7 @@ module SpeckleConnector
# Mesh group id helps to determine how to group faces into meshes.
# @param face [Sketchup::Face] face to get mesh group id.
def self.get_mesh_group_id(face, model_preferences)
if model_preferences[:include_entity_attributes]
if model_preferences[:include_entity_attributes] && model_preferences[:include_face_entity_attributes]
has_attribute_dictionary = !(face.attribute_dictionaries.nil? || face.attribute_dictionaries.first.nil?)
return face.persistent_id.to_s if has_attribute_dictionary
end
@@ -45,7 +45,7 @@ module SpeckleConnector
# @param group [Sketchup::Group] group to convert Speckle BlockInstance
def self.from_group(group, units, component_defs, preferences, &convert)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
if preferences[:model][:include_entity_attributes] && preferences[:model][:include_group_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler.attribute_dictionaries_to_speckle(group)
end
att = dictionaries.any? ? { dictionaries: dictionaries } : {}
@@ -66,7 +66,8 @@ module SpeckleConnector
# rubocop:disable Metrics/MethodLength
def self.from_component_instance(component_instance, units, component_defs, preferences, &convert)
dictionaries = {}
if preferences[:model][:include_entity_attributes]
if preferences[:model][:include_entity_attributes] &&
preferences[:model][:include_component_entity_attributes]
dictionaries = SketchupModel::Dictionary::DictionaryHandler
.attribute_dictionaries_to_speckle(component_instance)
end
+2
View File
@@ -18,6 +18,7 @@ require_relative '../actions/reload_accounts'
require_relative '../actions/load_saved_streams'
require_relative '../actions/init_local_accounts'
require_relative '../actions/collect_preferences'
require_relative '../actions/collect_versions'
module SpeckleConnector
module Ui
@@ -65,6 +66,7 @@ module SpeckleConnector
remove_stream: Commands::RemoveStream.new(@app),
notify_connected: Commands::NotifyConnected.new(@app),
collect_preferences: Commands::ActionCommand.new(@app, Actions::CollectPreferences),
collect_versions: Commands::ActionCommand.new(@app, Actions::CollectVersions),
user_preferences_updated: Commands::UserPreferencesUpdated.new(@app),
model_preferences_updated: Commands::ModelPreferencesUpdated.new(@app)
}.freeze
+23 -6
View File
@@ -80,7 +80,11 @@
</v-menu>
</v-app-bar>
<create-stream v-if="accounts().length !== 0"/>
<create-stream-dialog
v-if="accounts().length !== 0"
:account-id="activeAccount().userInfo.id"
:server-url="activeAccount().serverInfo.url"
/>
<v-container v-if="accounts().length !== 0" fluid>
<router-view :stream-search-query="streamSearchQuery" />
@@ -104,13 +108,19 @@ global.collectPreferences = function (preferences) {
bus.$emit('update-preferences', preferences)
}
global.collectVersions = function (versions) {
let vers = JSON.parse(versions)
localStorage.setItem('hostAppVersion', vers.sketchup)
localStorage.setItem('speckleVersion', vers.speckle)
}
global.loadAccounts = function (accounts) {
console.log('>>> SpeckleSketchup: Loading accounts', accounts)
localStorage.setItem('localAccounts', JSON.stringify(accounts))
let uuid = localStorage.getItem('uuid')
if (accounts.length !== 0){
if (uuid) {
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] == uuid))
global.setSelectedAccount(accounts.find((acct) => acct['userInfo']['id'] === uuid))
} else {
global.setSelectedAccount(accounts.find((acct) => acct['isDefault']))
}
@@ -129,8 +139,8 @@ export default {
name: 'App',
components: {
Login,
CreateStream: () => import('@/components/CreateStream'),
SettingsDialog: () => import('@/components/SettingsDialog'),
CreateStreamDialog: () => import('@/components/dialogs/CreateStreamDialog'),
SettingsDialog: () => import('@/components/dialogs/SettingsDialog'),
GlobalToast: () => import('@/components/GlobalToast')
},
props: {
@@ -176,15 +186,22 @@ export default {
this.$vuetify.theme.dark = this.preferences.user.dark_theme
})
// Collect versions to inform mixpanel
sketchup.exec({name: "collect_versions", data: {}})
// Collect preferences to render UI according to it
sketchup.exec({name: "collect_preferences", data: {}})
// this.$vuetify.theme.dark = localStorage.getItem('theme') == 'dark'
// Collect accounts from 'Accounts.db' by ruby sqlite3
sketchup.exec({name: "init_local_accounts", data: {}})
},
methods: {
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
activeAccount(){
return this.accounts().find((account) => account['isDefault'])
},
switchAccount(account) {
this.$mixpanel.track('Connector Action', { name: 'Account Select' })
global.setSelectedAccount(account)
+13 -2
View File
@@ -1,6 +1,8 @@
<template>
<v-snackbar v-model="snack" app bottom color="primary">
{{ text }}
<v-snackbar v-if="text" v-model="snack" app bottom :color="color">
<div v-for="(line, index) in text.split('\n')" :key="index">
{{ line }}
</div>
<template #action="{}">
<v-btn v-if="actionName" small outlined @click="openUrl(url)" @click:append="snack = false">
{{ actionName }}
@@ -16,6 +18,7 @@ export default {
data() {
return {
snack: false,
color: "primary",
text: null,
actionName: null,
url: null
@@ -33,6 +36,14 @@ export default {
mounted() {
this.$eventHub.$on('notification', (args) => {
this.snack = true
this.color = "primary"
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
})
this.$eventHub.$on('error', (args) => {
this.snack = true
this.color = "#CC3300"
this.text = args.text
this.actionName = args.action ? args.action.name : null
this.url = args.action ? args.action.url : null
+31 -10
View File
@@ -55,16 +55,22 @@
<timeago class="mr-1" :datetime="stream.updatedAt" />
|
<v-icon class="ml-1" small>mdi-account-key-outline</v-icon>
{{ stream.role.split(':')[1] }}
{{ stream.role === null ? "" : stream.role.split(':')[1] }}
</v-card-text>
<v-card-text class="d-flex align-center pb-5 mb-5 -mt-2" style="height: 50px">
<v-menu offset-y>
<template #activator="{ on, attrs }">
<v-slide-x-transition>
<div v-show="hover">
<create-branch-dialog :stream-name="stream.name" :stream-id="streamId"/>
</div>
</v-slide-x-transition>
<v-chip v-if="stream.branches" small v-bind="attrs" class="mr-1" v-on="on">
<v-icon small class="mr-1 float-left">mdi-source-branch</v-icon>
{{ branchName }}
</v-chip>
</template>
<!-- Branch list -->
<v-list dense>
<v-list-item
v-for="(branch, index) in stream.branches.items"
@@ -73,7 +79,7 @@
@click="switchBranch(branch.name)"
>
<v-list-item-title class="text-caption font-weight-regular">
<v-icon v-if="branch.name == branchName" small class="mr-1 float-left">
<v-icon v-if="branch.name === branchName" small class="mr-1 float-left">
mdi-check
</v-icon>
<v-icon v-else small class="mr-1 float-left">mdi-source-branch</v-icon>
@@ -171,6 +177,9 @@ global.oneClickSend = function (streamId) {
export default {
name: 'StreamCard',
components: {
CreateBranchDialog: () => import('@/components/dialogs/CreateBranchDialog'),
},
props: {
streamId: {
type: String,
@@ -261,16 +270,28 @@ export default {
},
computed: {
selectedBranch() {
if (this.$apollo.loading) return
return this.stream.branches.items.find((branch) => branch.name == this.branchName)
if (!this.stream) return
return this.stream.branches.items.find((branch) => branch.name === this.branchName)
},
selectedCommit() {
if (this.$apollo.loading) return
if (this.commitId == 'latest') return this.selectedBranch.commits.items[0]
return this.selectedBranch.commits.items.find((commit) => commit.id == this.commitId)
if (!this.selectedBranch) return
if (this.commitId === 'latest') return this.selectedBranch.commits.items[0]
return this.selectedBranch.commits.items.find((commit) => commit.id === this.commitId)
}
},
mounted() {
bus.$on(`refresh-stream-${this.streamId}`, () => {
let oldBranchName = this.branchName
let oldCommitId = this.commitId
this.$apollo.queries.stream.refetch()
this.branchName = oldBranchName
this.commitId = oldCommitId
})
bus.$on(`create-branch-${this.streamId}`, (branchName) => {
this.$apollo.queries.stream.refetch()
this.branchName = branchName
this.commitId = 'latest'
})
bus.$on(`sketchup-objects-${this.streamId}`, async (batches, commitId, totalChildrenCount) => {
console.log('>>> SpeckleSketchUp: Received objects from sketchup')
@@ -376,11 +397,11 @@ export default {
await this.sleep(2000)
},
async createCommit(batches, commitId, totalChildrenCount) {
if (batches.length == 0) {
if (batches.length === 0) {
this.loadingSend = false
this.loadingStage = null
this.$eventHub.$emit('notification', {
text: 'No objects selected. Nothing was sent.'
text: 'No objects selected. Nothing was sent.\n'
})
return
}
@@ -434,7 +455,7 @@ export default {
})
console.log('>>> SpeckleSketchUp: Sent to stream: ' + this.streamId, commit)
this.$eventHub.$emit('notification', {
text: 'Model selection sent!',
text: 'Model selection sent!\n',
action: {
name: 'View in Web',
url: `${localStorage.getItem('serverUrl')}/streams/${this.streamId}/commits/${
@@ -0,0 +1,134 @@
<template>
<!-- DIALOG: Create Branch -->
<v-dialog v-model="showCreateBranch">
<template #activator="{ on: dialog, attrs }">
<v-btn
v-tooltip="'Create Branch'"
icon x-small class="ml-0 mr-1"
v-bind="attrs"
v-on="{...dialog}"
>
<v-icon>
mdi-plus-circle
</v-icon>
</v-btn>
</template>
<v-card>
<v-card-title class="text-h5 mb-1">
Create a New Branch
</v-card-title>
<v-card-subtitle class="py-0 my-0 font-italic">
under {{ streamName }} stream
</v-card-subtitle>
<v-container class="px-6" pb-0>
<v-text-field
v-model="branchName"
xxxclass="small-text-field"
hide-details
dense
flat
placeholder="Branch Name"
/>
<v-text-field
v-model="description"
xxxclass="small-text-field"
hide-details
dense
flat
placeholder="Description (Optional)"
/>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue darken-1"
text
@click="showCreateBranch = false"
>
Cancel
</v-btn>
<v-btn
:disabled="branchName === ''"
color="blue darken-1"
text
@click="createBranch"
>
Create
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import gql from "graphql-tag";
import {bus} from "@/main";
export default {
name: "CreateBranchDialog",
props: {
streamId: {
type: String,
default: null
},
streamName: {
type: String,
default: null
}
},
data() {
return {
showCreateBranch: false,
branchName: "",
description: "",
defaultDescription: "Stream created from SketchUp",
accountToCreateStream: null
}
},
computed: {
loggedIn() {
return localStorage.getItem('SpeckleSketchup.AuthToken') !== null
}
},
methods: {
async createBranch(){
let res = await this.$apollo.mutate({
mutation: gql`
mutation branchCreate($branch: BranchCreateInput!) {
branchCreate(branch: $branch)
}
`,
variables: {
branch: {
streamId: this.streamId,
name: this.branchName,
description: this.description === '' ? this.defaultDescription : this.description,
}
}
})
bus.$emit(`create-branch-${this.streamId}`, this.branchName)
this.showCreateBranch = false
this.branchName = ""
this.description = ""
this.$mixpanel.track('Connector Action', { name: 'Create Branch' })
return res
}
}
}
</script>
<style>
.v-dialog {
max-width: 390px
}
.v-text-field >>> input {
font-size: 0.9em;
}
.v-text-field >>> label {
font-size: 0.9em;
}
</style>
@@ -92,7 +92,6 @@
</v-dialog>
<!-- DIALOG: Add a Stream by ID or URL -->
<!--
<v-dialog v-model="showCreateStreamById">
<template #activator="{ on, attrs }">
<v-btn
@@ -141,14 +140,13 @@
:disabled="createStreamByIdText === ''"
color="blue darken-1"
text
@click="showCreateStreamById = false"
@click="getStream"
>
Add
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
-->
</v-col>
</v-row>
</v-container>
@@ -159,9 +157,20 @@
import gql from "graphql-tag";
import {bus} from "@/main";
import userQuery from "@/graphql/user.gql";
import {StreamWrapper} from "@/utils/streamWrapper";
export default {
name: "CreateStream",
name: "CreateStreamDialog",
props: {
accountId: {
type: String,
default: null
},
serverUrl: {
type: String,
default: null
}
},
data() {
return {
showCreateNewStream: false,
@@ -177,10 +186,7 @@ export default {
computed: {
loggedIn() {
return localStorage.getItem('SpeckleSketchup.AuthToken') !== null
},
accounts() {
return JSON.parse(localStorage.getItem('localAccounts'))
},
}
},
apollo: {
user: {
@@ -188,16 +194,70 @@ export default {
}
},
methods: {
activeAccount(){
return JSON.parse(localStorage.getItem('selectedAccount'))
},
switchAccountToCreateStream(account) {
this.accountToCreateStream = account
},
refresh() {
this.$apollo.queries.user.refetch()
bus.$emit('refresh-streams')
},
async getStream(){
try {
const streamWrapper = new StreamWrapper(this.createStreamByIdText, this.accountId, this.serverUrl)
let res = await this.$apollo.query({
query: gql`
query Stream($id: String!){
stream(id: $id){
id
name
description
isPublic
role
createdAt
updatedAt
commentCount
favoritedDate
favoritesCount
collaborators {
id
name
role
avatar
},
branches (limit: ${10}){
totalCount,
cursor,
items {
id,
name,
description,
commits {
totalCount
}
}
}
}
}
`,
variables: {
id: streamWrapper.streamId
}
})
let stream = res.data.stream
this.$eventHub.$emit('notification', {
text: 'Stream Added by URL!\n',
})
bus.$emit('stream-added-by-id-or-url', stream.id)
}
catch (e){
this.$eventHub.$emit('error', {
text: 'The stream you are trying to add might;\n' +
'- lies on different server, \n' +
'- be private, \n' +
'- not be existed anymore.',
})
}
this.showCreateStreamById = false
},
async createStream(){
let res = await this.$apollo.mutate({
mutation: gql`
@@ -216,6 +276,7 @@ export default {
this.showCreateNewStream = false
this.streamName = ""
this.description = ""
this.$mixpanel.track('Connector Action', { name: 'Create Stream' })
sketchup.exec({name: "save_stream", data: {stream_id: res["data"]["streamCreate"]}})
this.refresh()
return res
@@ -35,6 +35,35 @@
class="pt-1 my-n5"
:label="'Include entity attributes'"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeEdgeAttributes"
class="pt-1 my-n5 ml-10"
:label="'Edge'"
:disabled="!includeAttributes"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeFaceAttributes"
class="pt-1 my-n5 ml-10"
:label="'Face'"
:disabled="!includeAttributes"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeGroupAttributes"
class="pt-1 my-n5 ml-10"
:label="'Group'"
:disabled="!includeAttributes"
/>
<v-icon class="ml-3" style="line-height: 0;">mdi-arrow-right-bottom</v-icon>
<v-switch
v-model="includeComponentAttributes"
class="pt-1 my-n5 ml-10"
:label="'Component'"
:disabled="!includeAttributes"
/>
<div class="sm1 mt-3">Receive Strategy</div>
<v-divider class="mb-2"/>
<v-switch
@@ -78,6 +107,10 @@ export default {
description: "",
combineFacesByMaterial: this.preferences.model.combine_faces_by_material,
includeAttributes: this.preferences.model.include_entity_attributes,
includeFaceAttributes: this.preferences.model.include_face_entity_attributes,
includeEdgeAttributes: this.preferences.model.include_edge_entity_attributes,
includeGroupAttributes: this.preferences.model.include_group_entity_attributes,
includeComponentAttributes: this.preferences.model.include_component_entity_attributes,
mergeCoplanarFaces: this.preferences.model.merge_coplanar_faces,
}
},
@@ -110,6 +143,46 @@ export default {
},
deep: true
},
'includeFaceAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_face_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Face Entity Attributes Option' })
},
deep: true
},
'includeEdgeAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_edge_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Edge Entity Attributes Option' })
},
deep: true
},
'includeGroupAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_group_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Group Entity Attributes Option' })
},
deep: true
},
'includeComponentAttributes': {
handler(newValue) {
sketchup.exec({
name: "model_preferences_updated",
data: {preference: "include_component_entity_attributes", value: newValue}
})
this.$mixpanel.track('Connector Action', { name: 'Include Component Entity Attributes Option' })
},
deep: true
},
'mergeCoplanarFaces': {
handler(newValue) {
sketchup.exec({
@@ -122,14 +195,11 @@ export default {
}
},
methods: {
combineFacesByMaterialHandler() {
},
switchTheme() {
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
sketchup.exec({
name: "user_preferences_updated",
data: {preference_hash: "configDUI", preference: "DarkTheme", value: this.$vuetify.theme.dark}
data: {preference_hash: "configSketchup", preference: "DarkTheme", value: this.$vuetify.theme.dark}
})
this.$mixpanel.track('Connector Action', { name: 'Toggle Theme' })
},
+15 -4
View File
@@ -10,9 +10,12 @@ const SpeckleMetrics = {
Vue.prototype.$mixpanel = mixpanel
Vue.prototype.$mixpanel.init(token, config)
Vue.prototype.$mixpanel.register({ hostApp: 'sketchup', type: 'action' })
Vue.prototype.$refreshMixpanelIds = function () {
// 1. It logs out the user and removes out the registered props.
Vue.prototype.$mixpanel.reset()
// 2. create hashes for user distinction
let distinctId =
'@' +
crypto
@@ -29,12 +32,20 @@ const SpeckleMetrics = {
.digest('hex')
.toUpperCase()
// 3. Setting super properties that will be sent with every event
Vue.prototype.$mixpanel.register({
// eslint-disable-next-line camelcase
distinct_id: distinctId,
// eslint-disable-next-line camelcase
hostApp: 'sketchup',
type: 'action',
hostAppVersion: localStorage.getItem('hostAppVersion'),
core_version: localStorage.getItem('speckleVersion'),
server_id: serverId
})
// 4. Logging into user
Vue.prototype.$mixpanel.identify(distinctId)
// 5. If it is a registered user we can consider it is identified
Vue.prototype.$mixpanel.people.set("Identified", true)
}
}
}
+77
View File
@@ -0,0 +1,77 @@
require('url')
export class StreamWrapper {
constructor(streamIdOrUrl, accountId, serverUrl) {
this.originalOutput = streamIdOrUrl
try {
this.streamWrapperFromUrl(streamIdOrUrl)
}
catch (e){
this.serverUrl = serverUrl
this.userId = accountId
this.streamId = streamIdOrUrl
}
}
streamWrapperFromUrl(streamUrl){
this.url = new URL(streamUrl)
this.segments = this.url.pathname.split('/').map((segment) => segment + '/')
this.serverUrl = this.url.origin
if (this.segments.length >= 4 && this.segments[3]?.toLowerCase() === "branches/"){
this.streamId = this.segments[2].replace("/", "")
if (this.segments.length > 5)
{
let branchSegments = this.segments.slice(4, this.segments.length - 1);
this.branchName = branchSegments.join("")
}
else
{
this.branchName = this.segments[4]
}
} else {
switch (this.segments.length){
case 3: // ie http://speckle.server/streams/8fecc9aa6d
if (this.segments[1].toLowerCase() === "streams/")
this.streamId = this.segments[2].replace("/", "");
else
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
break;
case 4: // ie https://speckle.server/streams/0c6ad366c4/globals/
if (this.segments[3].toLowerCase().startsWith("globals"))
{
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[3].replace("/", "");
}
else
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
break;
case 5: // ie http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179
switch (this.segments[3].toLowerCase()){
case "commits/":
this.streamId = this.segments[2].replace("/", "");
this.commitId = this.segments[4].replace("/", "");
break;
case "globals/":
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[3].replace("/", "");
this.commitId = this.segments[4].replace("/", "");
break;
case "branches/":
this.streamId = this.segments[2].replace("/", "");
this.branchName = this.segments[4].replace("/", "");
break;
case "objects/":
this.streamId = this.segments[2].replace("/", "");
this.objectId = this.segments[4].replace("/", "");
break;
default:
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
}
break;
default:
throw new Error(`Cannot parse ${this.originalOutput} into a stream wrapper class`);
}
}
}
}
+13 -1
View File
@@ -73,12 +73,24 @@ export default {
},
mounted() {
bus.$on('refresh-streams', () => {
// TODO: We should remember selected branches and commits before refetch
this.$apollo.queries.streams.refetch()
// TODO: We should set previously selected branches and commits after refetch
})
bus.$on('set-saved-streams', (streamIds) => {
this.savedStreams = streamIds
})
bus.$on('stream-added-by-id-or-url', (streamId) => {
if (!this.savedStreams){
this.savedStreams = []
this.savedStreams.push(streamId)
} else {
if (!this.savedStreams.includes(streamId)){
this.savedStreams.push(streamId)
}
}
sketchup.exec({name: "save_stream", data: {stream_id: streamId}})
})
sketchup.exec({name: "load_saved_streams", data: {}})
console.log('LAUNCHED')
this.$mixpanel.track('Connector Action', { name: 'Launched' })